jlcodes99/cockpit-tools · error
Token 交换请求失败: {}
Error message
Token 交换请求失败: {} What it means
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.
Source
Thrown at crates/cockpit-core/src/modules/oauth.rs:126
let client = crate::utils::http::create_client(15);
let (client_id, client_secret, client_key) = oauth_client_config(None)?;
let params = [
("client_id", client_id),
("client_secret", client_secret),
("code", code),
("redirect_uri", redirect_uri),
("grant_type", "authorization_code"),
];
let response = client
.post(TOKEN_URL)
.form(¶ms)
.send()
.await
.map_err(|e| {
let msg = format!("Token 交换请求失败: {}", e);
crate::modules::logger::log_error(&msg);
msg
})?;
let status = response.status();
crate::modules::logger::log_info(&format!("Token 交换响应状态: {}", status));
if status.is_success() {
let mut token_res = response.json::<TokenResponse>().await.map_err(|e| {
let msg = format!("Token 解析失败: {}", e);
crate::modules::logger::log_error(&msg);
msg
})?;
token_res.oauth_client_key = Some(client_key);
if token_res.refresh_token.is_some() {
crate::modules::logger::log_info("Token 交换成功, 获取到 refresh_token");
} else {
crate::modules::logger::log_warn(View on GitHub (pinned to 1ed8b77992)
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.
Example fix
// before: surfacing the raw error to the user
match exchange_code(&code, &verifier).await {
Err(e) => ui.show(e),
Ok(t) => save(t),
}
// after: classify network failure and offer retry
match exchange_code(&code, &verifier).await {
Err(e) if e.contains("交换请求失败") => ui.show_retryable("网络错误,请检查连接后重试", e),
Err(e) => ui.show(e),
Ok(t) => save(t),
} Defensive patterns
Strategy: retry
Validate before calling
// Probe connectivity to the token endpoint before exchanging the code
async fn token_endpoint_reachable(url: &str) -> bool {
reqwest::Client::new().get(url).timeout(std::time::Duration::from_secs(5))
.send().await.map(|r| r.status().as_u16() < 500).unwrap_or(false)
} Type guard
fn is_network_transport_error(err: &str) -> bool {
err.starts_with("Token 交换请求失败: ")
} Try / catch
match exchange_code(&code, &verifier).await {
Err(e) if is_network_transport_error(&e) => {
if backoff_retry(|| exchange_code(&code, &verifier), 3).is_err() {
ui.show("网络异常,请检查连接/代理后重试登录");
}
}
other => handle(other),
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of jlcodes99/cockpit-tools@1ed8b77992 (2026-09-05).
Data as JSON: /api/errors/a585f6ec17fe543a.
Report an issue: GitHub.