{"record":{"id":"e4210e081b23ff24","repo":"Kuberwastaken/claurst","slug":"refresh-http","errorCode":null,"errorMessage":"refresh: HTTP {} — {}","messagePattern":"refresh: HTTP (.+?) — (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-rust/crates/mcp/src/oauth.rs","lineNumber":544,"sourceCode":"        .refresh_token\n        .as_deref()\n        .ok_or_else(|| anyhow::anyhow!(\"Token for {} has no refresh token\", server_name))?\n        .to_string();\n\n    let client = reqwest::Client::new();\n    let params = [(\"grant_type\", \"refresh_token\"), (\"refresh_token\", refresh.as_str())];\n\n    let resp = client\n        .post(token_endpoint)\n        .form(&params)\n        .send()\n        .await\n        .map_err(|e| anyhow::anyhow!(\"refresh: request failed: {}\", e))?;\n\n    if !resp.status().is_success() {\n        let status = resp.status();\n        let body = resp.text().await.unwrap_or_default();\n        anyhow::bail!(\"refresh: HTTP {} — {}\", status, body);\n    }\n\n    #[derive(serde::Deserialize)]\n    struct TokenResponse {\n        access_token: String,\n        refresh_token: Option<String>,\n        expires_in: Option<u64>,\n    }\n\n    let tr: TokenResponse = resp.json().await?;\n    let expires_at = tr.expires_in.map(|s| {\n        std::time::SystemTime::now()\n            .duration_since(std::time::UNIX_EPOCH)\n            .unwrap_or_default()\n            .as_secs()\n            + s\n    });\n","sourceCodeStart":526,"sourceCodeEnd":562,"githubUrl":"https://github.com/Kuberwastaken/claurst/blob/b0637c97ec34144387cbf2f74f65df6d16a6cef1/src-rust/crates/mcp/src/oauth.rs#L526-L562","documentation":"Refreshing an MCP OAuth token via the refresh_token grant returned a non-success HTTP status. The library includes the status and response body so the provider's error (commonly invalid_grant) is visible. On this failure the cached token cannot be renewed and re-authorization is required.","triggerScenarios":"refresh_mcp_token (called by get_valid_mcp_token when the stored access token is expired) POSTs the refresh token and receives a non-2xx status.","commonSituations":"Refresh token revoked or expired (provider rotation invalidates old tokens); user revoked the app in provider settings; client credentials changed; token endpoint URL wrong or provider returning 5xx.","solutions":["Re-run the MCP OAuth login flow for the server to obtain a new refresh token","Check the response body for invalid_grant and, if present, treat stored tokens as revoked","Verify client_id/client_secret and token endpoint URL are still correct","For 5xx responses, retry after confirming the provider is healthy"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"match get_valid_mcp_token(server).await {\n    Err(e) if e.to_string().starts_with(\"refresh: HTTP\") && e.to_string().contains(\"invalid_grant\") => {\n        // clear stored tokens and trigger interactive re-auth\n    }\n    other => other?,\n}","preventionTips":["Re-authenticate periodically; refresh tokens rotate and old ones get revoked","Do not reuse refresh tokens across machines or sessions","Monitor for invalid_grant in response bodies as a signal to re-auth"],"tags":["oauth","http","refresh-token","network"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"b0637c97ec34144387cbf2f74f65df6d16a6cef1","analyzedAt":"2026-09-10T00:24:58.650Z","contentChangedAt":"2026-09-10T00:24:58.650Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}