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(&params)
        .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

  1. Check the wrapped reqwest message for the concrete cause (dns error, connection refused, timeout) and fix connectivity first.
  2. Verify proxy environment variables (HTTP_PROXY/HTTPS_PROXY) are correct or unset as appropriate.
  3. Retry the exchange — authorization codes are short-lived, so restart the login flow if the code expired.
  4. Confirm the auth endpoint domain is reachable (`curl -v $TOKEN_URL`) and not blocked by firewall/VPN policy.
  5. 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

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.