BerriAI/litellm · error · Exception
Validation fails: {e}
Error message
Validation fails: {e} What it means
Raised in _auth_jwt_with_issuer as the generic decode-failure handler: PyJWT rejected the token for any reason other than expiry - most commonly InvalidSignatureError (wrong public key), InvalidAudienceError (aud mismatch with issuer_config.audience), InvalidIssuerError (iss mismatch), or DecodeError (malformed token). The underlying PyJWT exception text is appended after 'Validation fails: ', naming the exact check that failed.
Source
Thrown at litellm/proxy/auth/handle_jwt.py:992
kid=kid,
)
try:
payload: Final = self._decode_jwt_with_public_key(
token=token,
public_key=public_key,
audience=issuer_config.audience,
issuer=issuer_config.issuer,
disable_audience_validation=issuer_config.disable_audience_validation,
)
except jwt.ExpiredSignatureError:
raise ProxyException(
message="Token Expired",
type=ProxyErrorTypes.expired_key,
param=None,
code=status.HTTP_401_UNAUTHORIZED,
)
except Exception as e:
raise Exception(f"Validation fails: {e}")
return self._apply_issuer_claim_mappings(
token=payload,
issuer_config=issuer_config,
)
async def auth_jwt(self, token: str) -> dict:
header: Final = jwt.get_unverified_header(token)
verbose_proxy_logger.debug("header: %s", header)
kid: Final = header.get("kid", None)
issuer_config: Final = self._get_configured_issuer(token=token)
if issuer_config is not None:
return await self._auth_jwt_with_issuer(
token=token,
issuer_config=issuer_config,View on GitHub (pinned to 77b7c6c40c)
Solutions
- Read the suffix after 'Validation fails:' - 'Signature verification failed', 'Audience doesn\u2019t match', 'Invalid issuer' each point to a different fix
- Decode the token (jwt.io) and compare its aud and iss against the issuer_config's audience/issuer values; align them
- For signature failures, confirm the token's kid resolves to the key you configured and that the JWKS is current post-rotation
- If multiple issuers are configured, check which issuer block matched (its audience/issuer) - the token may need to be sent with a config whose values match its claims
Example fix
# config.yaml - before: audience does not match the token's aud claim
litellm_jwtauth:
issuer_configs:
- issuer: https://idp.example.com
audience: my-api-v1
# config.yaml - after: audience matches the token
litellm_jwtauth:
issuer_configs:
- issuer: https://idp.example.com
audience: https://my-api.example.com/v2 Defensive patterns
Strategy: validation
Validate before calling
import jwt as pyjwt
def token_matches_issuer_config(token: str, audience: str, issuer: str) -> None:
payload = pyjwt.decode(token, options={"verify_signature": False})
if audience and payload.get("aud") not in (audience if isinstance(audience, list) else [audience]):
raise ValueError(f"token aud={payload.get('aud')!r} does not match configured audience={audience!r}")
if issuer and payload.get("iss") != issuer:
raise ValueError(f"token iss={payload.get('iss')!r} does not match configured issuer={issuer!r}") Try / catch
# the PyJWT reason is appended after 'Validation fails:' - branch on it
try:
await call_proxy(bearer_token)
except Exception as e:
msg = str(e)
if "Validation fails:" in msg:
if "Audience" in msg:
fix_audience_config() # align issuer_config.audience with token aud
elif "Signature" in msg:
await refresh_jwks() # stale/wrong signing key
else:
raise
else:
raise Prevention
- Keep issuer_config audience/issuer values in lockstep with the IdP's aud/iss - alert on IdP-side claim changes
- Verify a sample token against the issuer config in CI before shipping config changes
- After key rotation, confirm the JWKS actually serves the new signing key
When it happens
Trigger: A JWT verified against an issuer_config whose public key did not sign the token, or whose audience/issuer settings do not match the token's aud/iss claims - e.g. a token minted for a different client_id sent where audience is that client_id, or the wrong issuer block matched the token.
Common situations: audience config drift after the IdP changes the API identifier; tokens from a different OIDC realm/application sharing an IdP; JWKS serving stale keys after rotation so signature verification fails; hand-edited tokens failing signature or format checks.
Related errors
- audience must be provided unless disable_audience_validation
- Unmapped scope type - {type(token['scope'])}. Supported type
- JWT Auth: OIDC discovery endpoint {url} returned status {res
- JWT Auth: Failed to parse OIDC discovery document at {url}:
- JWT Auth: OIDC discovery document at {url} does not contain
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/761b08a2ae8d7b8c.
Report an issue: GitHub.