{"record":{"id":"7de5241dfe691199","repo":"BerriAI/litellm","slug":"invalid-jwt-submitted","errorCode":null,"errorMessage":"Invalid JWT Submitted","messagePattern":"Invalid JWT Submitted","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"litellm/proxy/auth/handle_jwt.py","lineNumber":1039,"sourceCode":"                    token=token,\n                    public_key=public_key,\n                    audience=decode_kwargs[\"audience\"],\n                    issuer=decode_kwargs[\"issuer\"],\n                    options=decode_kwargs[\"options\"],\n                )\n                return {k: v for k, v in payload.items() if k not in self.LITELLM_INTERNAL_CLAIMS}\n\n            except jwt.ExpiredSignatureError:\n                raise ProxyException(\n                    message=\"Token Expired\",\n                    type=ProxyErrorTypes.expired_key,\n                    param=None,\n                    code=status.HTTP_401_UNAUTHORIZED,\n                )\n            except Exception as e:\n                raise Exception(f\"Validation fails: {e}\")\n\n        raise Exception(\"Invalid JWT Submitted\")\n\n    async def close(self):\n        await self.http_handler.close()\n\n\nclass JWTAuthManager:\n    \"\"\"Manages JWT authentication and authorization operations\"\"\"\n\n    @staticmethod\n    def can_rbac_role_call_route(\n        rbac_role: RBAC_ROLES,\n        general_settings: dict,\n        route: str,\n    ) -> Literal[True]:\n        \"\"\"\n        Checks if user is allowed to access the route, based on their role.\n        \"\"\"\n        role_based_routes: Final = get_role_based_routes(rbac_role=rbac_role, general_settings=general_settings)","sourceCodeStart":1021,"sourceCodeEnd":1057,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/auth/handle_jwt.py#L1021-L1057","documentation":"Raised at the end of auth_jwt when get_public_key returned None instead of a key or an exception: no usable public key was resolved for the token, so verification cannot even be attempted and the token is rejected as 'Invalid JWT Submitted'. Practically this surfaces when the key-lookup path yields nothing - e.g. an empty/blank JWT_PUBLIC_KEY_URL value that produces an empty URL list, or a JWKS response whose parse_keys found nothing to return.","triggerScenarios":"JWT auth is active without issuer_configs, get_public_key(kid) resolves to None (blank env var entries, empty keys payload), and the code falls through the 'if public_key is not None' block to this raise.","commonSituations":"JWT_PUBLIC_KEY_URL set but containing only whitespace/commas so the URL list is empty; JWKS endpoint returning an empty keys structure; defensive fallback after partially-failed key resolution.","solutions":["Ensure JWT_PUBLIC_KEY_URL is set to a non-empty, valid JWKS or discovery URL (check for stray whitespace or commas)","curl the JWKS URL and confirm it returns at least one key (a non-empty keys array)","Prefer configuring keys via litellm_jwtauth in the config YAML if env-var handling is unreliable in your deployment"],"exampleFix":"# before: whitespace-only value yields an empty URL list\nJWT_PUBLIC_KEY_URL=\" , \"\n\n# after: a real JWKS endpoint\nJWT_PUBLIC_KEY_URL=https://idp.example.com/protocol/openid-connect/certs","handlingStrategy":"validation","validationCode":"import os\n\ndef jwt_key_sources_are_configured() -> None:\n    raw = os.getenv(\"JWT_PUBLIC_KEY_URL\", \"\")\n    urls = [u.strip() for u in raw.split(\",\") if u.strip()]\n    if not urls:\n        raise ValueError(\"JWT_PUBLIC_KEY_URL unset or blank - configure a JWKS/discovery URL\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Lint JWT_PUBLIC_KEY_URL for empty/whitespace values in deployment checks","Canary the JWKS endpoint at startup to confirm it returns at least one key","Prefer config-yaml litellm_jwtauth public-key settings over loosely validated env strings"],"tags":["jwt","jwks","public-key","configuration","authentication"],"backgroundTag":"jwt-signing-key-not-found","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}