{"record":{"id":"58c208bf715e0067","repo":"BerriAI/litellm","slug":"jwt-auth-failed-to-parse-oidc-discovery-document","errorCode":null,"errorMessage":"JWT Auth: Failed to parse OIDC discovery document at {url}: {e}","messagePattern":"JWT Auth: Failed to parse OIDC discovery document at (.+?): (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"litellm/proxy/auth/handle_jwt.py","lineNumber":628,"sourceCode":"        \"\"\"\n        if \".well-known/openid-configuration\" not in url:\n            return url\n\n        cache_key: Final = f\"litellm_oidc_discovery_{url}\"\n        cached_jwks_uri: Final = await self.user_api_key_cache.async_get_cache(cache_key)\n        if cached_jwks_uri is not None:\n            return cached_jwks_uri\n\n        verbose_proxy_logger.debug(\"JWT Auth: Fetching OIDC discovery document from %s\", url)\n        response: Final = await self.http_handler.get(url)\n        if response.status_code != 200:\n            raise Exception(\n                f\"JWT Auth: OIDC discovery endpoint {url} returned status {response.status_code}: {response.text}\"\n            )\n        try:\n            discovery: Final = response.json()\n        except Exception as e:\n            raise Exception(f\"JWT Auth: Failed to parse OIDC discovery document at {url}: {e}\")\n\n        jwks_uri: Final = discovery.get(\"jwks_uri\")\n        if not jwks_uri:\n            raise Exception(f\"JWT Auth: OIDC discovery document at {url} does not contain a 'jwks_uri' field.\")\n\n        verbose_proxy_logger.debug(\"JWT Auth: Resolved OIDC discovery %s -> jwks_uri=%s\", url, jwks_uri)\n        await self.user_api_key_cache.async_set_cache(\n            key=cache_key,\n            value=jwks_uri,\n            ttl=self._get_public_key_cache_ttl(),\n        )\n        return jwks_uri\n\n    def _get_public_key_cache_ttl(self) -> float:\n        litellm_jwtauth: Final = getattr(self, \"litellm_jwtauth\", None)\n        if litellm_jwtauth is None:\n            return 600\n        return litellm_jwtauth.public_key_ttl","sourceCodeStart":610,"sourceCodeEnd":646,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/auth/handle_jwt.py#L610-L646","documentation":"Raised in _resolve_jwks_url when the OIDC discovery endpoint returned HTTP 200 but response.json() failed - the body is not valid JSON. The original parse error is chained into the message. This means the URL resolved and answered, but what it returned is not a discovery document.","triggerScenarios":"JWT_PUBLIC_KEY_URL contains .well-known/openid-configuration and the server responds 200 with HTML (a login page, an SPA shell, a proxy block page) or truncated/invalid JSON, so json decoding raises.","commonSituations":"A gateway/SSO portal in front of the IdP serving an HTML page for unauthenticated fetchers; a typo'd URL that matches some catch-all route returning 200 HTML; response corruption through a corporate proxy.","solutions":["curl -s <url> | python -m json.tool from the proxy host to see the raw body and where JSON parsing fails","If HTML comes back, the URL is being intercepted - use the true IdP discovery URL or the JWKS URL directly","Ensure the fetch is unauthenticated (no auth wall) or whitelist the proxy's egress for that host"],"exampleFix":"# before: URL that returns an HTML login page with 200\nJWT_PUBLIC_KEY_URL=https://sso.example.com/.well-known/openid-configuration\n\n# after: the IdP's real discovery endpoint (or direct JWKS URL)\nJWT_PUBLIC_KEY_URL=https://idp.example.com/oidc/realms/prod/.well-known/openid-configuration","handlingStrategy":"validation","validationCode":"import httpx\n\nasync def discovery_returns_json(url: str) -> bool:\n    async with httpx.AsyncClient() as c:\n        r = await c.get(url)\n        if r.status_code != 200:\n            return False\n        try:\n            r.json()\n            return True\n        except ValueError:\n            return False  # HTML login/block page served with 200","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Smoke-test IdP endpoints with curl and inspect the body, not just the status code","Watch for SSO gateways that answer 200 with HTML for unauthenticated clients","Point JWT_PUBLIC_KEY_URL at the direct JWKS URL when discovery is unreliable"],"tags":["jwt","oidc","discovery","json","authentication"],"backgroundTag":"invalid-json-response","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}