Kuberwastaken/claurst · error
Failed to exchange code
Error message
Failed to exchange code: {} What it means
Wraps a reqwest transport failure that occurred while POSTing the OAuth authorization code (plus code_verifier and redirect_uri) to OpenAI's Codex token endpoint. The library uses anyhow to append the underlying reqwest error to the message. It means the HTTP request itself never completed, so no token status code was ever received.
Solutions
- Check basic network connectivity (curl https://auth.openai.com) and any HTTP(S)_PROXY environment variables.
- Retry the OAuth login flow; transient network failures resolve on retry.
- If behind a corporate proxy, configure reqwest-compatible proxy env vars or add the corporate CA to the trust store.
- If it persists, verify the CODEX_TOKEN_URL host is not blocked/renamed by your DNS provider.
Example fix
// before
let resp = client.post(CODEX_TOKEN_URL).form(¶ms).send().await.map_err(|e| anyhow!("Failed to exchange code: {}", e))?;
// after
let resp = client.post(CODEX_TOKEN_URL)
.form(¶ms)
.timeout(Duration::from_secs(30))
.send()
.await
.map_err(|e| anyhow!("Failed to exchange code: {}", e))?; Defensive patterns
Strategy: retry
Try / catch
// retry with backoff on transport failure
for attempt in 0..3 {
match client.post(CODEX_TOKEN_URL).form(¶ms).send().await {
Ok(resp) => break resp,
Err(e) if attempt < 2 => tokio::time::sleep(Duration::from_secs(2u64 << attempt)).await,
Err(e) => return Err(anyhow!("Failed to exchange code: {}", e)),
}
} Prevention
- Verify network/proxy reachability of the token endpoint before starting the OAuth flow
- Set a request timeout so failures are fast and diagnosable
- Trust the corporate CA in the client's TLS config when behind an intercepting proxy
When it happens
Trigger: In exchange_code_for_tokens, client.post(CODEX_TOKEN_URL).form(¶ms).send() returns Err: DNS failure, connection refused/reset, TLS error, or request timeout while contacting the token URL during run_oauth_flow_with_label.
Common situations: No network access or offline machine; corporate proxy or firewall blocking the token endpoint; DNS misconfiguration; TLS interception with untrusted CA; transient server outage of the OAuth provider.
Related errors
- Token exchange failed
- Token exchange failed
- exchange_code: HTTP
- models endpoint returned
- Bridge register: server returned
AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10).
Data as JSON: /api/errors/282b20d49bf027cc.
Report an issue: GitHub.
Appendix: source
Thrown at src-rust/crates/cli/src/codex_oauth_flow.rs:200
}
/// Exchange authorization code for access tokens.
async fn exchange_code_for_tokens(code: &str, verifier: &str) -> anyhow::Result<CodexTokens> {
let client = reqwest::Client::new();
let params = [
("client_id", CODEX_CLIENT_ID),
("code", code),
("code_verifier", verifier),
("grant_type", "authorization_code"),
("redirect_uri", CODEX_REDIRECT_URI),
];
let resp = client
.post(CODEX_TOKEN_URL)
.form(¶ms)
.send()
.await
.map_err(|e| anyhow!("Failed to exchange code: {}", e))?;
if !resp.status().is_success() {
let status = resp.status();
let body = resp.text().await.unwrap_or_default();
bail!("Token exchange failed ({}): {}", status, body);
}
let body: serde_json::Value = resp
.json()
.await
.map_err(|e| anyhow!("Failed to parse token response: {}", e))?;
let access_token = body["access_token"]
.as_str()
.unwrap_or("")
.to_string();
if access_token.is_empty() {
View on GitHub (pinned to b0637c97ec)