googleapis/mcp-toolbox · error
failed to parse introspection response: %w
Error message
failed to parse introspection response: %w
What it means
The introspection response body was read, but json.Unmarshal into the expected shape ({active, scope, aud, audience, exp, iss}) failed, meaning the body is not valid JSON or does not match. The library requires RFC 7662-style introspection JSON to decide whether the token is active.
Source
Thrown at internal/auth/generic/generic.go:414
return nil, &MCPAuthError{Code: http.StatusUnauthorized, Message: fmt.Sprintf("introspection failed with status: %d", resp.StatusCode), ScopesRequired: a.ScopesRequired}
}
body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
if err != nil {
return nil, fmt.Errorf("failed to read introspection response: %w", err)
}
var introspectResp struct {
Active *bool `json:"active"`
Scope string `json:"scope"`
Aud json.RawMessage `json:"aud"`
Audience json.RawMessage `json:"audience"`
Exp json.Number `json:"exp"`
Iss string `json:"iss"`
}
if err := json.Unmarshal(body, &introspectResp); err != nil {
return nil, fmt.Errorf("failed to parse introspection response: %w", err)
}
if introspectResp.Active == nil || !*introspectResp.Active {
logger.InfoContext(ctx, "token is not active")
return nil, &MCPAuthError{Code: http.StatusUnauthorized, Message: "token is not active", ScopesRequired: a.ScopesRequired}
}
var expVal int64
if introspectResp.Exp != "" {
expVal, err = introspectResp.Exp.Int64()
if err != nil {
logger.WarnContext(ctx, "failed to parse exp claim in introspection response: %v", err)
return nil, &MCPAuthError{Code: http.StatusUnauthorized, Message: "invalid exp claim", ScopesRequired: a.ScopesRequired}
}
}
// Verify expiration (with 1 minute leeway)
const leeway = 60View on GitHub (pinned to 8cc6e09de2)
Solutions
- curl the introspection endpoint with a valid token and confirm the body is RFC 7662 JSON containing an 'active' boolean.
- Fix the introspectionUrl/authorizationServer config so it points at the actual introspection API, not a login or error page.
- Check the auth server's status/logs — a 200-with-HTML response usually signals a gateway or misrouting problem.
Example fix
// before: endpoint returns HTML
<html><body>404 Not Found</body></html>
// after: correct introspection endpoint returns RFC 7662 JSON
{"active":true,"scope":"read write","exp":1893456000} Defensive patterns
Strategy: validation
Validate before calling
resp, err := http.PostForm(cfg.IntrospectionURL, url.Values{"token": {testToken}})
if err != nil { return err }
defer resp.Body.Close()
ct := resp.Header.Get("Content-Type")
if !strings.Contains(ct, "application/json") {
return fmt.Errorf("introspection endpoint returned %q, expected JSON — check the URL points at the RFC 7662 API", ct)
} Type guard
func isRFC7662Response(body []byte) bool {
var r struct{ Active *bool `json:"active"` }
return json.Unmarshal(body, &r) == nil && r.Active != nil
} Try / catch
claims, err := svc.ValidateMCPAuth(ctx, header)
if err != nil {
if strings.Contains(err.Error(), "failed to parse introspection response") {
// introspection endpoint returned non-JSON: verify URL points at the introspection API
return claims, fmt.Errorf("introspection endpoint misconfigured: %w", err)
}
return claims, err
} Prevention
- Point introspectionUrl at the token-introspection API, never a UI or userinfo route
- Curl the endpoint once at deploy time and confirm RFC 7662 JSON with an 'active' field
- Check Content-Type is application/json on introspection responses
When it happens
Trigger: The configured introspection endpoint returns non-JSON content (HTML login/error page, empty body, plain text), or JSON whose structure breaks decoding into the anonymous struct — then the wrapped error is returned from validateOpaqueToken.
Common situations: introspectionUrl pointing at a UI route instead of the token-introspection API, an auth-server error page (502/503 HTML) returned with 200, wrong path (e.g. '/userinfo' instead of '/introspect'), or a proxy injecting HTML.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- failed to create introspection request: %w
- Failed to encode PRM response
- params must be a valid JSON string: %w
- failed to marshal result: %w
- failed to construct introspection URL: %w
AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05).
Data as JSON: /api/errors/2745f74a3e05afc3.
Report an issue: GitHub.