zeroclaw-labs/zeroclaw · error · anyhow::Error
Missing OAuth code in callback
Error message
Missing OAuth code in callback
What it means
`parse_code_from_redirect` classified the input as a callback payload (contains `?` or has `code`/`state`/`error` params) but found no `code` parameter, so there is nothing to exchange for tokens. Non-callback raw input is returned as-is; only callback-shaped input without a code fails. This prevents an empty or partial redirect from being treated as a successful login.
Source
Thrown at crates/zeroclaw-providers/src/auth/openai_oauth.rs:337
if let Some(expected_state) = expected_state {
if let Some(got) = params.get("state") {
if got != expected_state {
anyhow::bail!("OAuth state mismatch");
}
} else if is_callback_payload {
anyhow::bail!("Missing OAuth state in callback");
}
}
if let Some(code) = params.get("code").cloned() {
return Ok(code);
}
if !is_callback_payload {
return Ok(trimmed.to_string());
}
anyhow::bail!("Missing OAuth code in callback")
}
pub fn extract_account_id_from_jwt(token: &str) -> Option<String> {
let payload = token.split('.').nth(1)?;
let decoded = base64::engine::general_purpose::URL_SAFE_NO_PAD
.decode(payload)
.ok()?;
let claims: serde_json::Value = serde_json::from_slice(&decoded).ok()?;
// Prefer the flat chatgpt_account_id claim when present.
if let Some(value) = claims.get("chatgpt_account_id").and_then(|v| v.as_str())
&& !value.trim().is_empty()
{
return Some(value.to_string());
}
// Real OpenAI OAuth tokens namespace custom claims under
// https://api.openai.com/auth as a JSON object, not a flat dotted key.View on GitHub (pinned to 88bb9c8533)
Solutions
- Restart the login flow and complete consent in the browser so the redirect carries `code`
- Do not navigate to the loopback callback URL manually
- Ignore callback-shaped requests that lack `code` (favicon/prefetch noise) instead of failing the flow
- If integrating, require the full authorize step to finish before parsing the callback path
Example fix
// before: first request to the listener aborts the whole login
let code = parse_code_from_redirect(path, Some(&state))?; // "Missing OAuth code in callback"
// after: keep listening when the hit carries no code
let code = match parse_code_from_redirect(path, Some(&state)) {
Ok(code) => code,
Err(e) if e.to_string().contains("Missing OAuth code") => continue_listening().await,
Err(e) => return Err(e),
}; Defensive patterns
Strategy: validation
Validate before calling
fn callback_has_code(input: &str) -> bool {
input.split_once('?').map_or(false, |(_, q)| {
q.split('&').any(|pair| pair.starts_with("code="))
})
}
if callback_has_code(path) {
let code = parse_code_from_redirect(path, Some(&pkce.state))?;
} Try / catch
match parse_code_from_redirect(path, Some(&state)) {
Ok(code) => code,
Err(e) if e.to_string().contains("Missing OAuth code") => continue_listening().await, // stray hit, keep the listener up
Err(e) => return Err(e),
} Prevention
- Treat only requests under /auth/callback carrying code= as login completion
- Ignore favicon and prefetch hits on the loopback listener
- Require consent to finish in the browser; never hand-navigate to the callback URL
When it happens
Trigger: `parse_code_from_redirect("/auth/callback?state=xyz", Some("xyz"))` — state validates but no `code` key exists in the parsed query. Any query-bearing path that reaches the final bail without a `code` param (the `error` and state checks already passed).
Common situations: User manually opens http://localhost:1455/auth/callback before completing consent; the IdP redirects after an aborted consent carrying only state; browser prefetch or a stray request lands on the loopback listener with a query string.
Related errors
- Missing OAuth state in callback
- No OAuth code provided
- Could not parse OAuth code from input
- OAuth state mismatch
- OpenAI OAuth token request failed ({status}): {body}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/f04223727232a595.
Report an issue: GitHub.