{"record":{"id":"d02a0629b2b0d931","repo":"BerriAI/litellm","slug":"token-validation-failed","errorCode":"token_validation_failed","errorMessage":"OAuth token rejected: required field '{key}' is absent","messagePattern":"OAuth token rejected: required field '(.+?)' is absent","errorType":"http","errorClass":"HTTPException","httpStatus":403,"severity":"error","filePath":"litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py","lineNumber":502,"sourceCode":"    (``\"org_id\": \"12345\"``).  Booleans are normalised to JSON-style ``\"true\"`` /\n    ``\"false\"`` so admin rules written as ``{\"verified\": \"true\"}`` match upstream\n    responses of ``{\"verified\": true}``.\n    \"\"\"\n    for key, expected in validation_rules.items():\n        actual: Any = token_response.get(key)\n        # Try dot-notation traversal when top-level lookup returns None\n        if actual is None and \".\" in key:\n            obj: Any = token_response\n            for part in key.split(\".\"):\n                if isinstance(obj, dict):\n                    obj = obj.get(part)\n                else:\n                    obj = None\n                    break\n            actual = obj\n        # Treat absent fields as a distinct failure from a mismatched value\n        if actual is None:\n            raise HTTPException(\n                status_code=403,\n                detail={\n                    \"error\": \"token_validation_failed\",\n                    \"server_id\": server_id,\n                    \"field\": key,\n                    \"message\": (f\"OAuth token rejected: required field '{key}' is absent\"),\n                },\n            )\n        if _normalize_for_token_comparison(actual) != _normalize_for_token_comparison(expected):\n            raise HTTPException(\n                status_code=403,\n                detail={\n                    \"error\": \"token_validation_failed\",\n                    \"server_id\": server_id,\n                    \"field\": key,\n                    \"message\": (f\"OAuth token rejected: '{key}' = '{actual}', expected '{expected}'\"),\n                },\n            )","sourceCodeStart":484,"sourceCodeEnd":520,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py#L484-L520","documentation":"Fires inside the token-response validation loop when a rule key (top-level or dot-notation path) resolves to nothing in the IdP's token response; the admin-declared validation rule cannot be evaluated, so the token is rejected as untrusted rather than silently passing.","triggerScenarios":"Thrown at litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py:502 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Ensure the upstream token response contains the required field (e.g. access_token, token_type).","Check the upstream provider's error response."],"exampleFix":"Log the token response body and verify required keys.","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}