jlcodes99/cockpit-tools · error

Token 交换失败: status={}, body_len={}

Error message

Token 交换失败: status={}, body_len={}

What it means

In exchange_code_for_token_internal, after sending the OAuth authorization code to the provider's token endpoint, the HTTP response status is not a success (2xx). The error is logged with the status code and response body length, and a formatted error is returned to complete_oauth_login. The authorization code could not be exchanged for access/id/refresh tokens.

Source

Thrown at crates/cockpit-core/src/modules/codex_oauth.rs:893

    logger::log_info("Codex OAuth 开始交换 Token");

    // 官方 authorization-code exchange 使用 raw auth client,不附加运行时 originator headers。
    let response = client
        .post(TOKEN_ENDPOINT)
        .form(&params)
        .send()
        .await
        .map_err(|e| format!("Token 请求失败: {}", e))?;

    let status = response.status();
    let body = response
        .text()
        .await
        .map_err(|e| format!("读取响应失败: {}", e))?;

    if !status.is_success() {
        logger::log_error(&format!(
            "Token 交换失败: status={}, body_len={}",
            status,
            body.len()
        ));
        return Err(format!(
            "Token 交换失败: status={}, body_len={}",
            status,
            body.len()
        ));
    }

    logger::log_info("Codex OAuth Token 交换成功");

    let token_response: serde_json::Value =
        serde_json::from_str(&body).map_err(|e| format!("解析 Token 响应失败: {}", e))?;

    let id_token = token_response
        .get("id_token")

View on GitHub (pinned to 1ed8b77992)

Solutions

  1. Restart the OAuth login flow to obtain a fresh authorization code and retry the exchange
  2. Verify redirect_uri and client parameters match exactly what was used in the authorize request
  3. Check provider status/endpoint availability if status is 5xx, then retry
  4. Inspect logs for the status code: 400/401 usually means invalid/expired code or misconfigured client
Defensive patterns

Strategy: retry

Validate before calling

// retry only on transient (5xx) failures; 4xx means the code/session must be redone
fn is_retryable_token_status(status: u16) -> bool { status >= 500 }

Try / catch

match start_oauth_login(app).await {
    Err(e) if format!("{e}").contains("Token 交换失败") => {
        if e.contains("status=5") {
            // provider outage: retry after a short backoff with a fresh code
            tokio::time::sleep(Duration::from_secs(2)).await;
            start_oauth_login(app).await
        } else {
            // 4xx: invalid/expired code or bad client config — restart flow, verify redirect_uri
            Err(e)
        }
    }
    other => other,
}

Prevention

When it happens

Trigger: Redeeming an expired or already-used authorization code; wrong redirect_uri / client_id in the token request; provider-side 4xx (invalid_grant, invalid_client) or 5xx outage; network proxy returning an error page.

Common situations: User took too long between authorize and exchange so the code expired; clock skew invalidating tokens; provider credentials rotated; OpenAI auth service temporarily down (5xx).

Related errors


AI-assisted analysis of jlcodes99/cockpit-tools@1ed8b77992 (2026-09-05). Data as JSON: /api/errors/add6bf90bf5d616a. Report an issue: GitHub.