{"record":{"id":"8488b79115552ed3","repo":"aaif-goose/goose","slug":"failed-to-exchange-code-8488b7","errorCode":null,"errorMessage":"Failed to exchange code: {} - {}","messagePattern":"Failed to exchange code: (.+?) - (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/goose/src/config/signup_tetrate/mod.rs","lineNumber":101,"sourceCode":"        eprintln!(\"Code: {}\", code);\n        eprintln!(\"Code verifier length: {}\", self.code_verifier.len());\n        eprintln!(\"Code challenge: {}\", self.code_challenge);\n\n        let response = client\n            .post(TETRATE_TOKEN_URL)\n            .header(\"X-Title\", \"goose\")\n            .header(\"Referer\", \"https://github.com/aaif-goose/goose\")\n            .json(&request_body)\n            .send()\n            .await?;\n\n        if !response.status().is_success() {\n            let status = response.status();\n            let error_text = response.text().await.unwrap_or_default();\n            eprintln!(\"Token exchange failed!\");\n            eprintln!(\"Status: {}\", status);\n            eprintln!(\"Error response: {}\", error_text);\n            return Err(anyhow!(\n                \"Failed to exchange code: {} - {}\",\n                status,\n                error_text\n            ));\n        }\n\n        let token_response: TokenResponse = response.json().await?;\n        Ok(token_response.key)\n    }\n\n    /// Complete flow: start server, open browser, wait for callback, exchange code\n    pub async fn complete_flow(&mut self) -> Result<String> {\n        let listener = tokio::net::TcpListener::bind((\"127.0.0.1\", 0)).await?;\n        let port = listener.local_addr()?.port();\n\n        let (code_tx, code_rx) = oneshot::channel::<String>();\n        let (shutdown_tx, shutdown_rx) = oneshot::channel::<()>();\n        self.server_shutdown_tx = Some(shutdown_tx);","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose/src/config/signup_tetrate/mod.rs#L83-L119","documentation":"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.","triggerScenarios":"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.","commonSituations":"Re-running exchange_code with a code that was already consumed; resuming an interrupted flow with a stale verifier; TARS temporarily unavailable.","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"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match flow.exchange_code(code).await {\n    Err(e) if e.to_string().starts_with(\"Failed to exchange code\") => {\n        // parse status/body from the message: spent/expired code -> rerun complete_flow();\n        // transient upstream 5xx -> retry the full flow after a short delay\n    }\n    other => other?,\n}","preventionTips":["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"],"tags":["auth","oauth","tetrate","network","http"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}