Kuberwastaken/claurst · error · anyhow::Error

exchange_code: bad JSON

Error message

exchange_code: bad JSON: {}

What it means

The token endpoint returned HTTP success but the response body was not valid JSON matching the expected TokenResponse shape (missing required access_token field, malformed JSON, or an HTML error page with a 200 status). The grant itself may have half-succeeded; the token cannot be deserialized.

Solutions

  1. Inspect the raw token endpoint response body (capture via logging/proxy) to see what was returned
  2. Verify the server implements RFC 6749 token responses with an access_token field
  3. Retry the flow; some servers intermittently return HTML error pages
Defensive patterns

Strategy: try-catch

When it happens

Trigger: Thrown at src-rust/crates/mcp/src/oauth.rs:502 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10). Data as JSON: /api/errors/6a00ac9fc58f2a09. Report an issue: GitHub.

Appendix: source

Thrown at src-rust/crates/mcp/src/oauth.rs:502

        .send()
        .await
        .map_err(|e| anyhow::anyhow!("exchange_code: request failed: {}", e))?;

    if !resp.status().is_success() {
        let status = resp.status();
        let body = resp.text().await.unwrap_or_default();
        anyhow::bail!("exchange_code: HTTP {} — {}", status, body);
    }

    #[derive(serde::Deserialize)]
    struct TokenResponse {
        access_token: String,
        refresh_token: Option<String>,
        expires_in: Option<u64>,
        scope: Option<String>,
    }

    let tr: TokenResponse = resp.json().await.map_err(|e| anyhow::anyhow!("exchange_code: bad JSON: {}", e))?;

    let expires_at = tr.expires_in.map(|secs| {
        std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap_or_default()
            .as_secs()
            + secs
    });

    Ok(McpToken {
        access_token: tr.access_token,
        refresh_token: tr.refresh_token,
        expires_at,
        scope: tr.scope,
        server_name: String::new(), // caller should set this
    })
}

View on GitHub (pinned to b0637c97ec)