aaif-goose/goose · error
Failed to exchange code: {} - {}
Error message
Failed to exchange code: {} - {} What it means
During Tetrate Agent Router Service (TARS) signup, the received authorization code is exchanged at the token endpoint with goose-identifying headers (X-Title: goose). A non-success HTTP status is surfaced as this error carrying the status code and the endpoint's response body, which are also printed to stderr.
Source
Thrown at crates/goose/src/config/signup_tetrate/mod.rs:101
eprintln!("Code: {}", code);
eprintln!("Code verifier length: {}", self.code_verifier.len());
eprintln!("Code challenge: {}", self.code_challenge);
let response = client
.post(TETRATE_TOKEN_URL)
.header("X-Title", "goose")
.header("Referer", "https://github.com/aaif-goose/goose")
.json(&request_body)
.send()
.await?;
if !response.status().is_success() {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
eprintln!("Token exchange failed!");
eprintln!("Status: {}", status);
eprintln!("Error response: {}", error_text);
return Err(anyhow!(
"Failed to exchange code: {} - {}",
status,
error_text
));
}
let token_response: TokenResponse = response.json().await?;
Ok(token_response.key)
}
/// Complete flow: start server, open browser, wait for callback, exchange code
pub async fn complete_flow(&mut self) -> Result<String> {
let listener = tokio::net::TcpListener::bind(("127.0.0.1", 0)).await?;
let port = listener.local_addr()?.port();
let (code_tx, code_rx) = oneshot::channel::<String>();
let (shutdown_tx, shutdown_rx) = oneshot::channel::<()>();
self.server_shutdown_tx = Some(shutdown_tx);View on GitHub (pinned to 3810898a74)
Solutions
- Inspect the embedded status and error body — invalid/expired code means you must rerun complete_flow() to obtain a fresh code and verifier
- Perform request and exchange within one flow object so code_verifier matches the code_challenge sent originally
- Retry the full flow later for transient 5xx responses
Defensive patterns
Strategy: retry
Try / catch
match flow.exchange_code(code).await {
Err(e) if e.to_string().starts_with("Failed to exchange code") => {
// parse status/body from the message: spent/expired code -> rerun complete_flow();
// transient upstream 5xx -> retry the full flow after a short delay
}
other => other?,
} Prevention
- Do not re-run exchange_code with a code that already succeeded once
- Keep request and exchange in one flow object so code_verifier matches
- Log the status and body (already on stderr) before retrying to distinguish auth errors from outages
When it happens
Trigger: The token POST returns 4xx/5xx: expired or already-redeemed code, PKCE verifier mismatch (verifier belongs to a different flow instance than the code), clock skew, or a TARS-side outage.
Common situations: Re-running exchange_code with a code that was already consumed; resuming an interrupted flow with a stale verifier; TARS temporarily unavailable.
Related errors
- Failed to exchange code: {} - {}
- Failed to receive authorization code
- Failed to receive authorization code
- HTTP error! status: ${response.status}
- Failed to fetch OIDC config: ${configResp.status}
AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16).
Data as JSON: /api/errors/8488b79115552ed3.
Report an issue: GitHub.