BerriAI/litellm · error · Exception
JWT Auth: OIDC discovery document at {url} does not contain
Error message
JWT Auth: OIDC discovery document at {url} does not contain a 'jwks_uri' field. What it means
Raised in _resolve_jwks_url when the OIDC discovery document was fetched and parsed as JSON successfully, but the parsed object has no jwks_uri field. The proxy needs jwks_uri to locate the signing keys, so key resolution aborts.
Source
Thrown at litellm/proxy/auth/handle_jwt.py:632
cache_key: Final = f"litellm_oidc_discovery_{url}"
cached_jwks_uri: Final = await self.user_api_key_cache.async_get_cache(cache_key)
if cached_jwks_uri is not None:
return cached_jwks_uri
verbose_proxy_logger.debug("JWT Auth: Fetching OIDC discovery document from %s", url)
response: Final = await self.http_handler.get(url)
if response.status_code != 200:
raise Exception(
f"JWT Auth: OIDC discovery endpoint {url} returned status {response.status_code}: {response.text}"
)
try:
discovery: Final = response.json()
except Exception as e:
raise Exception(f"JWT Auth: Failed to parse OIDC discovery document at {url}: {e}")
jwks_uri: Final = discovery.get("jwks_uri")
if not jwks_uri:
raise Exception(f"JWT Auth: OIDC discovery document at {url} does not contain a 'jwks_uri' field.")
verbose_proxy_logger.debug("JWT Auth: Resolved OIDC discovery %s -> jwks_uri=%s", url, jwks_uri)
await self.user_api_key_cache.async_set_cache(
key=cache_key,
value=jwks_uri,
ttl=self._get_public_key_cache_ttl(),
)
return jwks_uri
def _get_public_key_cache_ttl(self) -> float:
litellm_jwtauth: Final = getattr(self, "litellm_jwtauth", None)
if litellm_jwtauth is None:
return 600
return litellm_jwtauth.public_key_ttl
async def _get_public_key_from_jwks_url(self, jwks_url: str, kid: str | None) -> dict:
resolved_jwks_url: Final = await self._resolve_jwks_url(jwks_url)
cache_key: Final = f"litellm_jwt_auth_keys_{resolved_jwks_url}"View on GitHub (pinned to 77b7c6c40c)
Solutions
- If you meant to use the JWKS directly, make sure JWT_PUBLIC_KEY_URL is the keys endpoint URL that does NOT contain .well-known/openid-configuration - discovery is only triggered by that substring
- If you want discovery, curl the URL and confirm the JSON top level includes jwks_uri
- Fix the IdP/gateway routing so the well-known path serves the genuine discovery document
Example fix
# before: JWKS URL mislabeled with the discovery substring, triggering discovery parsing JWT_PUBLIC_KEY_URL=https://idp.example.com/jwks/.well-known/openid-configuration-certs # after: plain JWKS URL - discovery code path is skipped entirely JWT_PUBLIC_KEY_URL=https://idp.example.com/oidc/jwks
Defensive patterns
Strategy: validation
Validate before calling
import httpx
async def discovery_doc_is_valid(url: str) -> bool:
async with httpx.AsyncClient() as c:
doc = (await c.get(url)).json()
return isinstance(doc, dict) and bool(doc.get("jwks_uri")) Prevention
- Remember discovery only triggers when the URL contains '.well-known/openid-configuration' - keep raw JWKS URLs free of that substring
- Verify a freshly copied discovery URL returns a document containing jwks_uri before deploying
When it happens
Trigger: JWT_PUBLIC_KEY_URL contains .well-known/openid-configuration but the endpoint returns some other valid JSON - e.g. a JWKS document itself ({"keys": [...]}) served under a matching path, or an unrelated API payload - which lacks jwks_uri.
Common situations: Pointing JWT_PUBLIC_KEY_URL at the JWKS endpoint while the URL string still contains .well-known/openid-configuration (forcing the discovery code path); a gateway that rewrites the discovery path to another JSON API; custom servers that reuse the well-known path for different payloads.
Related errors
- JWT Auth: OIDC discovery endpoint {url} returned status {res
- JWT Auth: Failed to parse OIDC discovery document at {url}:
- MCPJWTSigner: access_token_discovery_uri discovery document
- Error parsing response: {e}. Check server logs for original
- No matching public key found. keys={resolved_jwks_url}, kid=
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/d68ed6fac3a03bb6.
Report an issue: GitHub.