{"record":{"id":"4d20b4aaa863d794","repo":"BerriAI/litellm","slug":"failed-to-fetch-oidc-userinfo-e","errorCode":null,"errorMessage":"Failed to fetch OIDC UserInfo: {e}","messagePattern":"Failed to fetch OIDC UserInfo: (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"litellm/proxy/auth/handle_jwt.py","lineNumber":783,"sourceCode":"\n            if response.status_code != 200:\n                raise Exception(f\"OIDC UserInfo endpoint returned status {response.status_code}: {response.text}\")\n\n            userinfo: Final = response.json()\n            verbose_proxy_logger.debug(\"Received OIDC UserInfo: %s\", userinfo)\n\n            # Cache the userinfo response\n            await self.user_api_key_cache.async_set_cache(\n                key=cache_key,\n                value=userinfo,\n                ttl=self.litellm_jwtauth.oidc_userinfo_cache_ttl,\n            )\n\n            return userinfo\n\n        except Exception as e:\n            verbose_proxy_logger.error(\"Error fetching OIDC UserInfo: %s\", e)\n            raise Exception(f\"Failed to fetch OIDC UserInfo: {e}\")\n\n    _unscoped_jwt_warning_emitted = False\n\n    @classmethod\n    def _build_decode_kwargs(cls) -> dict:\n        \"\"\"Build the audience/issuer/options kwargs for ``jwt.decode``.\n\n        Setting ``JWT_AUDIENCE`` (and optionally ``JWT_ISSUER``) turns on the\n        corresponding PyJWT verifications, blocking cross-tenant tokens\n        minted by other applications that share the same IdP signing keys.\n        When both are unset PyJWT only checks the signature and expiry, which\n        is preserved for backward compatibility but logged once as a warning.\n\n        The warning fires even in mixed deployments that also configure\n        ``LiteLLM_JWTAuth.issuers``: tokens whose ``iss`` does not match any\n        configured issuer fall through to this global path, and if env-var\n        scoping is absent that fallback is itself unscoped.\n        \"\"\"","sourceCodeStart":765,"sourceCodeEnd":801,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/auth/handle_jwt.py#L765-L801","documentation":"Catch-all raised in get_userinfo: any exception during the UserInfo exchange that is not the explicit non-200 branch (network errors, DNS failures, TLS problems, timeouts, or unexpected parse/encoding errors) is logged ('Error fetching OIDC UserInfo: ...') and re-raised wrapped as 'Failed to fetch OIDC UserInfo: <cause>'. The chained cause text identifies the underlying failure.","triggerScenarios":"The proxy cannot reach oidc_userinfo_endpoint at all - DNS resolution failure, connection refused, TLS handshake error, or HTTP client timeout - while handling a JWT-authenticated request that requires UserInfo claims.","commonSituations":"Egress firewall or NetworkPolicy blocking the IdP host from the proxy pod; wrong DNS/internal hostname for the IdP inside Kubernetes; self-signed cert on the IdP failing verification; IdP slow enough to hit the client timeout.","solutions":["Read the wrapped cause in the message - it distinguishes DNS vs connection vs SSL vs timeout","From the proxy container, curl the UserInfo endpoint to verify network paths, DNS, and TLS trust","Allowlist the IdP domain in egress rules / NetworkPolicies and add the IdP CA to the container trust store if using a private CA","For slow IdPs, raise the HTTP client timeout used for UserInfo fetches"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"import httpx, socket\n\ndef userinfo_endpoint_reachable(url: str) -> bool:\n    host = httpx.URL(url).host\n    try:\n        socket.getaddrinfo(host, 443)\n        return True\n    except socket.gaierror:\n        return False","typeGuard":null,"tryCatchPattern":"import asyncio\n\nasync def fetch_userinfo_with_retry(fetch, attempts=3):\n    for i in range(attempts):\n        try:\n            return await fetch()\n        except Exception as e:\n            if \"Failed to fetch OIDC UserInfo\" in str(e) and i < attempts - 1:\n                await asyncio.sleep(2 ** i)  # transient network/DNS/TLS - back off\n                continue\n            raise","preventionTips":["Verify egress from the proxy pod to the IdP host (DNS, firewall, NetworkPolicy) before enabling UserInfo","Trust the IdP's CA in the proxy container when using a private CA","Distinguish transient causes by the wrapped text (DNS/connection/SSL/timeout) before retrying"],"tags":["oidc","userinfo","network","dns","tls","authentication"],"backgroundTag":"oidc-userinfo-endpoint-failed","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}