{"record":{"id":"2745f74a3e05afc3","repo":"googleapis/mcp-toolbox","slug":"failed-to-parse-introspection-response-w","errorCode":null,"errorMessage":"failed to parse introspection response: %w","messagePattern":"failed to parse introspection response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/auth/generic/generic.go","lineNumber":414,"sourceCode":"\t\treturn nil, &MCPAuthError{Code: http.StatusUnauthorized, Message: fmt.Sprintf(\"introspection failed with status: %d\", resp.StatusCode), ScopesRequired: a.ScopesRequired}\n\t}\n\n\tbody, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read introspection response: %w\", err)\n\t}\n\n\tvar introspectResp struct {\n\t\tActive   *bool           `json:\"active\"`\n\t\tScope    string          `json:\"scope\"`\n\t\tAud      json.RawMessage `json:\"aud\"`\n\t\tAudience json.RawMessage `json:\"audience\"`\n\t\tExp      json.Number     `json:\"exp\"`\n\t\tIss      string          `json:\"iss\"`\n\t}\n\n\tif err := json.Unmarshal(body, &introspectResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse introspection response: %w\", err)\n\t}\n\n\tif introspectResp.Active == nil || !*introspectResp.Active {\n\t\tlogger.InfoContext(ctx, \"token is not active\")\n\t\treturn nil, &MCPAuthError{Code: http.StatusUnauthorized, Message: \"token is not active\", ScopesRequired: a.ScopesRequired}\n\t}\n\n\tvar expVal int64\n\tif introspectResp.Exp != \"\" {\n\t\texpVal, err = introspectResp.Exp.Int64()\n\t\tif err != nil {\n\t\t\tlogger.WarnContext(ctx, \"failed to parse exp claim in introspection response: %v\", err)\n\t\t\treturn nil, &MCPAuthError{Code: http.StatusUnauthorized, Message: \"invalid exp claim\", ScopesRequired: a.ScopesRequired}\n\t\t}\n\t}\n\n\t// Verify expiration (with 1 minute leeway)\n\tconst leeway = 60","sourceCodeStart":396,"sourceCodeEnd":432,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/auth/generic/generic.go#L396-L432","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: endpoint returns HTML\n<html><body>404 Not Found</body></html>\n// after: correct introspection endpoint returns RFC 7662 JSON\n{\"active\":true,\"scope\":\"read write\",\"exp\":1893456000}","handlingStrategy":"validation","validationCode":"resp, err := http.PostForm(cfg.IntrospectionURL, url.Values{\"token\": {testToken}})\nif err != nil { return err }\ndefer resp.Body.Close()\nct := resp.Header.Get(\"Content-Type\")\nif !strings.Contains(ct, \"application/json\") {\n    return fmt.Errorf(\"introspection endpoint returned %q, expected JSON — check the URL points at the RFC 7662 API\", ct)\n}","typeGuard":"func isRFC7662Response(body []byte) bool {\n    var r struct{ Active *bool `json:\"active\"` }\n    return json.Unmarshal(body, &r) == nil && r.Active != nil\n}","tryCatchPattern":"claims, err := svc.ValidateMCPAuth(ctx, header)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to parse introspection response\") {\n        // introspection endpoint returned non-JSON: verify URL points at the introspection API\n        return claims, fmt.Errorf(\"introspection endpoint misconfigured: %w\", err)\n    }\n    return claims, err\n}","preventionTips":["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"],"tags":["oauth","introspection","json"],"backgroundTag":"introspection-response-parse-failed","analyzedSha":"8cc6e09de2ad7b8bffc77751799585a1401a48eb","analyzedAt":"2026-09-05T01:10:36.887Z","contentChangedAt":"2026-09-05T01:10:36.887Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}