{"record":{"id":"f38f6545127f57e9","repo":"jlcodes99/cockpit-tools","slug":"token-status-body-len-f38f65","errorCode":null,"errorMessage":"Token 交换失败: status={}, body_len={}","messagePattern":"Token 交换失败: status=(.+?), body_len=(.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/src/modules/codex_oauth.rs","lineNumber":1078,"sourceCode":"    let status = response.status();\n    let body = response\n        .text()\n        .await\n        .map_err(|e| format!(\"读取响应失败: {}\", e))?;\n\n    if !status.is_success() {\n        let response_summary = serde_json::from_str::<serde_json::Value>(&body)\n            .map(|value| crate::modules::codex_auth_diagnostic::oauth_response_summary(&value))\n            .unwrap_or_else(|_| serde_json::json!({\"body_type\":\"non_json\"}));\n        crate::modules::codex_auth_diagnostic::log_event(\n            \"oauth_token_exchange_failed\",\n            serde_json::json!({\n                \"status\": status.as_u16(),\n                \"body_length\": body.len(),\n                \"response\": response_summary,\n            }),\n        );\n        logger::log_error(&format!(\n            \"Token 交换失败: status={}, body_len={}\",\n            status,\n            body.len()\n        ));\n        return Err(format!(\n            \"Token 交换失败: status={}, body_len={}\",\n            status,\n            body.len()\n        ));\n    }\n\n    logger::log_info(\"Codex OAuth Token 交换成功\");\n\n    let token_response: serde_json::Value =\n        serde_json::from_str(&body).map_err(|e| format!(\"解析 Token 响应失败: {}\", e))?;\n\n    crate::modules::codex_auth_diagnostic::log_event(\n        \"oauth_token_exchange_response\",","sourceCodeStart":1060,"sourceCodeEnd":1096,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src-tauri/src/modules/codex_oauth.rs#L1060-L1096","documentation":"exchange_code_for_token_internal posts the authorization code to Codex's token endpoint; when the HTTP response status is not a success, it logs and returns 'Token 交换失败: status={}, body_len={}'. This is a server-side rejection of the code exchange (invalid/expired code, bad client credentials, wrong redirect_uri) and the response body is summarized for debugging.","triggerScenarios":"Calling complete_oauth_login after the callback when the token endpoint returns 400/401/403/5xx: the code was already used or expired, PKCE verifier mismatch, client_id/client_secret wrong, redirect_uri not identical to the one used in the authorization request, or Codex token service outage.","commonSituations":"Retrying a login whose code was already exchanged (code is single-use); clock skew invalidating PKCE/claims; environment misconfiguration pointing at a staging token endpoint; provider rotating client secrets; user taking longer than the code's ~1-10 min validity.","solutions":["Read the logged response_summary/body to see the OAuth error code (invalid_grant, invalid_client, etc.)","Restart the whole login flow to get a fresh authorization code instead of reusing it","Verify client_id/client_secret and that redirect_uri matches the authorization request exactly","Check PKCE code_verifier/state handling for corruption or mismatch between sessions","Check Codex service status if status is 5xx, then retry"],"exampleFix":"// before\nreturn Err(format!(\"Token 交换失败: status={}, body_len={}\", status, body.len()));\n// after\nreturn Err(format!(\"Token 交换失败: status={}, detail={}\", status,\n    truncate_log_text(&response_summary, 256)));","handlingStrategy":"try-catch","validationCode":"// Before exchanging, validate prerequisites\nfn can_exchange(code_age_secs: u64, code_used: bool) -> Result<(), &'static str> {\n    if code_used { return Err(\"authorization code already consumed\"); }\n    if code_age_secs > 300 { return Err(\"authorization code likely expired\"); }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":"match complete_oauth_login(args).await {\n    Err(e) if e.contains(\"Token 交换失败\") => {\n        // parse status from message; 4xx => restart full OAuth flow, 5xx => retry later\n        if e.contains(\"status=5\") { schedule_retry(); } else { restart_oauth_flow(); }\n    }\n    Ok(t) => store_tokens(t),\n    Err(e) => eprintln!(\"{}\", e),\n}","preventionTips":["Never reuse an authorization code — a fresh code per exchange","Keep redirect_uri byte-identical between authorize and token requests","Verify client credentials and PKCE verifier storage before exchanging","Watch provider status pages; add retry only for 429/5xx"],"tags":["oauth","token-exchange","http-error","pkce"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"1ed8b77992d62ca81fabf744deb0839ad361d5bf","analyzedAt":"2026-09-05T09:51:41.178Z","contentChangedAt":"2026-09-05T09:51:41.178Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}