Diagnose Google OAuth redirect_uri_mismatch failures in SpotDraft when login, signup, or trial OAuth flows break in QA, dev, or production. Use this skill for Google Error 400 redirect_uri_mismatch, double slashes in redirect_uri (`//api/v1/...`), QA-only or prod-wide Google login failures after an oogway deploy, suspected missing callback registration, Pydantic HttpUrl trailing-slash side effects, or regressions around oogway's join_url helper.
This skill covers Google OAuth failures where Google rejects the redirect_uri before control returns to SpotDraft. The key distinction is whether Oogway generated a malformed callback URL or whether the callback URL is valid but missing from the Google OAuth client configuration.
Error 400: redirect_uri_mismatchoogway deploy, dependency upgrade, or cherry-pickBrowser -> Angular -> Oogway redirect endpoint
-> Google validates redirect_uri
-> on success returns to Oogway callback
-> Oogway forwards callback to the correct SpotDraft cluster
Primary router paths:
app/api/endpoints/proxies/oauth.pyapp/api/endpoints/proxies/oauth.pyapp/trial/presentation/views.py and app/trial/domain/handle_oauth_trial_signup_use_case.pyThis is the higher-probability failure mode when the incident follows an oogway rollout.
What happens:
settings.OAUTH_REDIRECT_BASE_URL is a HttpUrlhttps://api.spotdraft.com//api/v1/sd_auth/oauth/callbackhttps://{router_host}//router/v1/trial/signup/oauth/callbackMost important evidence:
redirect_uri shown on Google's error page is malformedjoin_url() fix or a recent rollout reintroduced raw concatenationThis is less common and should only be assumed when the URI shown on the Google error page is already syntactically correct.
What happens:
Most important evidence:
redirect_uri shown on the Google page is correct-looking and single-slashoogway rollout that could explain malformed joinsredirect_uri_mismatch by itself does not prove a Google Console misconfiguration.
The 2026-03-26 Chargebee QA incident (SPD-42842) was initially diagnosed as a missing Google callback registration, but it was ultimately fixed by rolling out oogway PR #131, the same code-level URL-joining fix that addressed the 2026-03-20 production outage (SPD-42603).
redirect_uriAsk support or QA for the Google error screenshot or the full failing URL.
Check:
redirect_uri contain // after the host?This step is the fastest way to separate malformed joins from genuine callback-registration gaps.
Check whether the issue affects:
oogway rolloutDo not over-index on "single customer reported it." The March 26 QA incident looked customer-specific at first, but support could reproduce it internally and the eventual fix was environment-wide.
join_url Fix Is Present in the Affected EnvironmentThe permanent fix was merged in SpotDraft/oogway PR #131 with merge commit:
f01f1c033acdfcff0a14f7f799e93f28345bebc1PR #131 introduced:
app/core/url_utils.pyjoin_url(base, *path_segments)And replaced raw URL concatenation in:
app/api/endpoints/proxies/oauth.pyapp/trial/presentation/views.pyapp/trial/domain/handle_oauth_trial_signup_use_case.pyapp/core/sd_api_client.pyapp/oauth/oidc_provider/domain/use_cases/get_oidc_config_use_case.pyapp/oauth/external/presentation/views.pyIf the affected environment does not have this fix, prioritize deployment or cherry-pick over Google Console changes.
Pre-fix broken pattern:
redirect_uri = f"{settings.OAUTH_REDIRECT_BASE_URL}/{path}"
Merged fix:
def join_url(base: str, *path_segments: str) -> str:
result = base.rstrip("/")
for segment in path_segments:
if not segment:
continue
seg = segment.lstrip("/")
if seg:
result = f"{result}/{seg}"
return result
Router OAuth now uses:
def _router_oauth_redirect_uri(router_callback_path: str) -> str:
assert settings.OAUTH_REDIRECT_BASE_URL is not None
return join_url(str(settings.OAUTH_REDIRECT_BASE_URL), router_callback_path)
Important operational detail:
OAUTH_REDIRECT_BASE_URL from HttpUrl to strGoogle rejects the malformed URI before a successful callback reaches SpotDraft, so logs are useful for correlation but often not decisive on their own.
GCP Cloud Logging - Prod oogway:
logName="projects/spotdraft-prod/logs/stderr"
labels."k8s-pod/app"="sd-apps-oogway"
jsonPayload.message:("oauth" OR "redirect" OR "callback")
timestamp >= "{incident_start_time}"
GCP Cloud Logging - QA oogway:
resource.type="k8s_container"
labels."k8s-pod/app"="spotdraft-qa-oogway"
textPayload:("oauth" OR "redirect" OR "callback")
timestamp >= "{incident_start_time}"
Groundcover example for QA EU:
qaqa-euqaspotdraft-qa-oogwayUseful correlation only:
| Signal | Malformed Oogway URL | Missing Google Console registration |
|---|---|---|
redirect_uri contains // after host | Strongly confirms | Rules out |
Incident follows oogway deploy / cherry-pick / dependency upgrade | Strongly supports | Weak |
Same env starts working immediately after PR #131 deploy | Confirms | Rules out |
redirect_uri is single-slash and correct for the env | Possible but weaker | Strongly supports |
| No code deploy, only Google Console update fixes it | Rules out | Confirms |
| User reaches SpotDraft callback and gets app/backend error | Rules out | Rules out |
oogway rollout, revert the rollout or revert the offending argo-manifests change.#131 (or an equivalent join_url patch) to the affected environment.Historical reference:
argo-manifests commit 6fa8925a87de822ff84d6ea0e8044e356b313875#131 was merged, verified in dev, then cherry-picked to QAAdd the exact affected environment callbacks in Google Cloud Console:
{oauth_redirect_base_url}/api/v1/sd_auth/oauth/callback{oauth_redirect_base_url}/api/v1/sd_auth/oauth/signup/callback{oauth_redirect_base_url}/{api_v1_prefix}/trial/signup/oauth/callbackNo deploy is required for this path, but verify whether the environment uses a shared or dedicated Google OAuth client before making the change.
PR #131 normalized URL building across more than just Google login. If you deploy the full patch, regression-test:
app/core/sd_api_client.pyTests added or updated in the merged fix:
app/tests/core/test_url_utils.pyapp/tests/api/proxies/test_oauth_proxy.pyapp/tests/trial/domain/test_handle_oauth_trial_signup_use_case.pyapp/tests/trial/presentation/test_trial_signup_views.pySPD-42603 - 2026-03-20 production Google login outage. Global impact. Root signal was double slash in redirect_uri. Short-term mitigation was revert; long-term fix was PR #131.SPD-42842 - 2026-03-26 Chargebee QA login failure on WSID 8243, cluster EU, env QA. Initially looked like a QA callback-registration issue, but final fix was the same join_url rollout, verified in dev and then QA on 2026-03-30.incident-lookup - if the symptom is ambiguous and you need to search for prior Slack/Jira incidents firstinfrastructure-alert-response - if the login incident coincides with pod restarts, latency spikes, or other infra symptoms instead of a clean Google OAuth rejection