Kuberwastaken/claurst · error · anyhow::Error

Token for has no refresh token

Error message

Token for {} has no refresh token

What it means

A stored token exists for the server but has no refresh_token field — the authorization server issued an access token only (no offline scope/grant). When the access token expires, it cannot be refreshed and the error surfaces from refresh_mcp_token.

Solutions

  1. Re-authenticate and request offline access / refresh-token scope if the server supports it
  2. If the server never issues refresh tokens, expect to re-authenticate when the access token expires
Defensive patterns

Strategy: fallback

When it happens

Trigger: Thrown at src-rust/crates/mcp/src/oauth.rs:528 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/0ac8c731d008beaa. Report an issue: GitHub.

Appendix: source

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

    });

    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
    })
}

/// Refresh an existing MCP token using the stored refresh token.
pub async fn refresh_mcp_token(server_name: &str, token_endpoint: &str) -> anyhow::Result<McpToken> {
    let existing = get_mcp_token(server_name)
        .ok_or_else(|| anyhow::anyhow!("No stored token for {}", server_name))?;
    let refresh = existing
        .refresh_token
        .as_deref()
        .ok_or_else(|| anyhow::anyhow!("Token for {} has no refresh token", server_name))?
        .to_string();

    let client = reqwest::Client::new();
    let params = [("grant_type", "refresh_token"), ("refresh_token", refresh.as_str())];

    let resp = client
        .post(token_endpoint)
        .form(&params)
        .send()
        .await
        .map_err(|e| anyhow::anyhow!("refresh: request failed: {}", e))?;

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

View on GitHub (pinned to b0637c97ec)