{"record":{"id":"a585f6ec17fe543a","repo":"jlcodes99/cockpit-tools","slug":"token-a585f6","errorCode":null,"errorMessage":"Token 交换请求失败: {}","messagePattern":"Token 交换请求失败: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/cockpit-core/src/modules/oauth.rs","lineNumber":126,"sourceCode":"    let client = crate::utils::http::create_client(15);\n    let (client_id, client_secret, client_key) = oauth_client_config(None)?;\n\n    let params = [\n        (\"client_id\", client_id),\n        (\"client_secret\", client_secret),\n        (\"code\", code),\n        (\"redirect_uri\", redirect_uri),\n        (\"grant_type\", \"authorization_code\"),\n    ];\n\n    let response = client\n        .post(TOKEN_URL)\n        .form(&params)\n        .send()\n        .await\n        .map_err(|e| {\n            let msg = format!(\"Token 交换请求失败: {}\", e);\n            crate::modules::logger::log_error(&msg);\n            msg\n        })?;\n\n    let status = response.status();\n    crate::modules::logger::log_info(&format!(\"Token 交换响应状态: {}\", status));\n\n    if status.is_success() {\n        let mut token_res = response.json::<TokenResponse>().await.map_err(|e| {\n            let msg = format!(\"Token 解析失败: {}\", e);\n            crate::modules::logger::log_error(&msg);\n            msg\n        })?;\n        token_res.oauth_client_key = Some(client_key);\n\n        if token_res.refresh_token.is_some() {\n            crate::modules::logger::log_info(\"Token 交换成功, 获取到 refresh_token\");\n        } else {\n            crate::modules::logger::log_warn(","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/crates/cockpit-core/src/modules/oauth.rs#L108-L144","documentation":"Returned by oauth::exchange_code when the HTTP POST to the OAuth TOKEN_URL itself fails (reqwest .send() error: DNS failure, connection refused/reset, TLS error, timeout). The reqwest Display error is wrapped as \"Token 交换请求失败: {}\" and propagated as Err(String), aborting the authorization-code exchange.","triggerScenarios":"Calling exchange_code (authorization code -> token) when the network request to TOKEN_URL cannot complete: offline machine, unreachable/blocked endpoint, DNS failure, TLS interception, proxy misconfiguration, or request timeout.","commonSituations":"Corporate proxy or firewall blocking the auth domain; VPN required but not connected; SSL-inspecting middlebox breaking TLS; transient network outage right after pasting the auth code; system clock issues causing TLS handshake failure.","solutions":["Check the wrapped reqwest message for the concrete cause (dns error, connection refused, timeout) and fix connectivity first.","Verify proxy environment variables (HTTP_PROXY/HTTPS_PROXY) are correct or unset as appropriate.","Retry the exchange — authorization codes are short-lived, so restart the login flow if the code expired.","Confirm the auth endpoint domain is reachable (`curl -v $TOKEN_URL`) and not blocked by firewall/VPN policy.","If TLS interception is the cause, trust the corporate CA or bypass inspection for the auth host."],"exampleFix":"// before: surfacing the raw error to the user\nmatch exchange_code(&code, &verifier).await {\n    Err(e) => ui.show(e),\n    Ok(t) => save(t),\n}\n// after: classify network failure and offer retry\nmatch exchange_code(&code, &verifier).await {\n    Err(e) if e.contains(\"交换请求失败\") => ui.show_retryable(\"网络错误，请检查连接后重试\", e),\n    Err(e) => ui.show(e),\n    Ok(t) => save(t),\n}","handlingStrategy":"retry","validationCode":"// Probe connectivity to the token endpoint before exchanging the code\nasync fn token_endpoint_reachable(url: &str) -> bool {\n    reqwest::Client::new().get(url).timeout(std::time::Duration::from_secs(5))\n        .send().await.map(|r| r.status().as_u16() < 500).unwrap_or(false)\n}","typeGuard":"fn is_network_transport_error(err: &str) -> bool {\n    err.starts_with(\"Token 交换请求失败: \")\n}","tryCatchPattern":"match exchange_code(&code, &verifier).await {\n    Err(e) if is_network_transport_error(&e) => {\n        if backoff_retry(|| exchange_code(&code, &verifier), 3).is_err() {\n            ui.show(\"网络异常，请检查连接/代理后重试登录\");\n        }\n    }\n    other => handle(other),\n}","preventionTips":["Verify network/VPN/proxy before starting an OAuth login, not after it fails.","Set HTTP_PROXY/HTTPS_PROXY correctly in sandboxed or corporate environments.","Exchange the code promptly — codes expire, and retries then need a fresh code.","Trust corporate CA certs if TLS inspection is deployed.","Retry transient send failures a limited number of times with backoff."],"tags":["oauth","network","http-request","tls"],"backgroundTag":"network-request-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"}