Kuberwastaken/claurst · error

No query string in callback

Error message

No query string in callback

What it means

The OAuth callback parser expects the browser's request line to be `GET /path?query HTTP/1.1` and locates the authorization code by finding the `?` that starts the query string. If the request path contains no `?`, there is no code/state to extract and the flow bails with this error. It usually means the browser hit the callback URL without the OAuth parameters.

Solutions

  1. Restart the OAuth flow and complete authorization in one pass — do not manually re-enter or refresh the callback URL.
  2. Check the authorization provider's redirect: if an error occurred, the flow should present it — retry login.
  3. Verify the auth URL was opened as generated (with its query parameters) rather than retyped.
  4. If a proxy or extension is stripping query params, disable it for localhost.

Example fix

// before (manual navigation)
// http://127.0.0.1:1455/auth/callback        -> No query string in callback
// after: use the full URL as produced by the flow
// http://127.0.0.1:1455/auth/callback?code=...&state=...
Defensive patterns

Strategy: validation

Validate before calling

// Only open the exact auth URL printed by the flow (it contains ?code=...&state=...)
// Verify in browser devtools that the final redirect URL retains its query string.

Try / catch

match run_oauth_flow(&mut app).await {
    Err(e) if e.to_string().contains("No query string in callback") => {
        eprintln!("Callback arrived without OAuth parameters; restart the login and use the printed URL as-is");
    }
    other => other?,
}

Prevention

When it happens

Trigger: `wait_for_callback` received a request line whose path has no query string: the user navigated to `http://127.0.0.1:<port>/` directly, the auth server redirected without parameters (error redirect handled elsewhere or stripped), or a health-check/probe hit the port.

Common situations: User manually refreshes or re-opens the callback URL after the query was dropped; browser extension strips URL parameters; authorization failed upstream and the redirect carried no `?code=`; port scanner or monitoring tool connecting to the local listener.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10). Data as JSON: /api/errors/bbcb02a7a684a8dd. Report an issue: GitHub.

Appendix: source

Thrown at src-rust/crates/cli/src/codex_oauth_flow.rs:134

        std::time::Duration::from_secs(300), // 5 minute timeout
        listener.accept(),
    )
    .await
    .map_err(|_| anyhow!("OAuth callback timeout (5 minutes)"))?
    .map_err(|e| anyhow!("Failed to accept connection: {}", e))?;

    let mut reader = BufReader::new(&mut socket);
    let mut request_line = String::new();
    reader.read_line(&mut request_line).await?;

    // Parse "GET /auth/callback?code=...&state=... HTTP/1.1"
    let parts: Vec<&str> = request_line.split_whitespace().collect();
    if parts.len() < 2 {
        bail!("Invalid HTTP request");
    }

    let path = parts[1];
    let query_start = path.find('?').ok_or_else(|| anyhow!("No query string in callback"))?;
    let query = &path[query_start + 1..];

    let mut code = String::new();
    let mut state = String::new();
    let mut error = String::new();

    for param in query.split('&') {
        let kv: Vec<&str> = param.splitn(2, '=').collect();
        if kv.len() == 2 {
            match kv[0] {
                "code" => code = urlencoding::decode(kv[1])?.to_string(),
                "state" => state = urlencoding::decode(kv[1])?.to_string(),
                "error" => error = urlencoding::decode(kv[1])?.to_string(),
                "error_description" => error = urlencoding::decode(kv[1])?.to_string(),
                _ => {}
            }
        }
    }

View on GitHub (pinned to b0637c97ec)