Kuberwastaken/claurst · error · anyhow::Error
OAuth metadata parse error
Error message
OAuth metadata parse error: {} What it means
When the well-known OAuth metadata endpoint responds successfully, the body is deserialized into serde_json::Value; a malformed or non-JSON body triggers this parse error. Fields like authorization_endpoint are then read with fallbacks, but the JSON itself must parse. Called by begin_mcp_auth and get_valid_mcp_token.
Solutions
- Verify the base URL is the server root so the .well-known path resolves to real metadata
- Check the response body manually with curl to see what is actually returned
- Confirm the server implements RFC 8414 authorization-server metadata at that URL
- Bypass intermediate proxies/gateways that rewrite the response body
Example fix
// before: hitting a path that returns HTML
let url = "https://example.com/mcp";
// after: metadata is discovered from the server root
let url = "https://mcp.example.com"; // fetches {url}/.well-known/oauth-authorization-server Defensive patterns
Strategy: fallback
Validate before calling
let resp = reqwest::get(format!("{base}/.well-known/oauth-authorization-server", base = server_url)).await?;
let ct = resp.headers().get(reqwest::header::CONTENT_TYPE)
.and_then(|v| v.to_str().ok()).unwrap_or("");
if !ct.contains("json") {
anyhow::bail!("metadata endpoint returned non-JSON content-type: {ct}");
} Try / catch
let meta: serde_json::Value = resp.json().await
.map_err(|e| e.context("metadata endpoint did not return JSON; check base_url and proxies"))?; Prevention
- Point the config url at the server root, not a page that returns HTML
- Verify with curl that .well-known/oauth-authorization-server returns JSON
- Bypass gateways that intercept 2xx with HTML challenge pages
- Rely on the built-in fallback endpoints when discovery body is unreliable
When it happens
Trigger: fetch_oauth_metadata() gets a 2xx response from {base_url}/.well-known/oauth-authorization-server whose body is not valid JSON — HTML error pages, empty bodies with 200, or wrong content served by a proxy.
Common situations: Server behind an auth gateway returning an HTML login page with 200; base_url pointing at the wrong path so a generic page is returned; server returning XML or plain text; CDN error page with success status.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Failed to parse token response
- Bridge register: server returned
- Bridge poll: auth error
- Token exchange failed
- No access_token in response
AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10).
Data as JSON: /api/errors/b6ebfd49a469e618.
Report an issue: GitHub.
Appendix: source
Thrown at src-rust/crates/mcp/src/oauth.rs:172
challenge,
)
}
pub async fn fetch_oauth_metadata(server_url: &str) -> anyhow::Result<McpOAuthMetadata> {
let base_url = normalized_server_url(server_url);
let fallback = fallback_oauth_metadata(base_url);
let metadata_url = format!("{}/.well-known/oauth-authorization-server", base_url);
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(10))
.build()
.map_err(|e| anyhow::anyhow!("Failed to build HTTP client: {}", e))?;
match client.get(&metadata_url).send().await {
Ok(resp) if resp.status().is_success() => {
let meta: serde_json::Value = resp
.json()
.await
.map_err(|e| anyhow::anyhow!("OAuth metadata parse error: {}", e))?;
Ok(McpOAuthMetadata {
authorization_endpoint: meta
.get("authorization_endpoint")
.and_then(|value| value.as_str())
.unwrap_or(fallback.authorization_endpoint.as_str())
.to_string(),
token_endpoint: meta
.get("token_endpoint")
.and_then(|value| value.as_str())
.unwrap_or(fallback.token_endpoint.as_str())
.to_string(),
})
}
Ok(_) | Err(_) => Ok(fallback),
}
}
pub async fn begin_mcp_auth(View on GitHub (pinned to b0637c97ec)