{"record":{"id":"75dc659e6221cfe4","repo":"PrefectHQ/fastmcp","slug":"jwks-uri-not-configured","errorCode":null,"errorMessage":"JWKS URI not configured","messagePattern":"JWKS URI not configured","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/auth/providers/jwt.py","lineNumber":348,"sourceCode":"\n    async def _get_verification_key(self, token: str) -> str | bytes:\n        \"\"\"Get the verification key for the token.\"\"\"\n        if self.public_key:\n            return self.public_key\n\n        # Extract kid from token header for JWKS lookup\n        try:\n            header = decode_jwt_header(token)\n            kid = header.get(\"kid\")\n            return await self._get_jwks_key(kid)\n\n        except (ValueError, KeyError, IndexError, json.JSONDecodeError) as e:\n            raise ValueError(f\"Failed to extract key ID from token: {e}\") from e\n\n    async def _get_jwks_key(self, kid: str | None) -> str:\n        \"\"\"Fetch key from JWKS with simple caching and SSRF protection.\"\"\"\n        if not self.jwks_uri:\n            raise ValueError(\"JWKS URI not configured\")\n\n        current_time = time.time()\n\n        # Check cache first\n        if current_time - self._jwks_cache_time < self._cache_ttl:\n            if kid and kid in self._jwks_cache:\n                return self._jwks_cache[kid]\n            elif not kid and len(self._jwks_cache) == 1:\n                # If no kid but only one key cached, use it\n                return next(iter(self._jwks_cache.values()))\n\n        # Fetch JWKS — with SSRF protection when enabled (untrusted URIs)\n        try:\n            jwks_data = await self._fetch_jwks()\n\n            # Cache all usable keys. A key that cannot be converted is skipped\n            # rather than failing the whole set — per RFC 7517 §5, clients\n            # should ignore JWKs they don't understand. Otherwise one exotic","sourceCodeStart":330,"sourceCodeEnd":366,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/providers/jwt.py#L330-L366","documentation":"This error means the JWT verification provider was asked to verify a token but its JWKS URI — the URL of the identity provider's JSON Web Key Set — was never set. The library throws it from _get_jwks_key because without a JWKS URI there is no way to fetch the public key needed to verify the token's signature. It is a configuration error in the provider, not a token problem.","triggerScenarios":"Calling verify_token (via _get_verification_key -> _get_jwks_key) on a JWTVerifier/TokenVerifier instance constructed without a jwks_uri, or where the jwks_uri argument was None/empty string.","commonSituations":"Constructing the verifier programmatically and omitting jwks_uri while also omitting secret_key/public_key; env-driven config where the JWKS variable is unset or empty; copying a verifier config that only specified an issuer; switching from a static-key verifier to a JWKS verifier without moving the URL over.","solutions":["Set jwks_uri in the verifier config to your identity provider's JWKS endpoint (e.g. https://idp.example.com/.well-known/jwks.json or the /.well-known/openid-configuration jwks_uri value)","Verify the config source: if jwks_uri comes from an env var or settings file, confirm it is populated and non-empty at startup","If you intend asymmetric verification, either provide jwks_uri or a static public_key/secret_key — one of them must be configured","Add a startup validation that instantiates the verifier and checks jwks_uri before the server accepts traffic"],"exampleFix":"// before\nverifier = JWTVerifier(issuer=\"https://idp.example.com\")\n// after\nverifier = JWTVerifier(\n    issuer=\"https://idp.example.com\",\n    jwks_uri=\"https://idp.example.com/.well-known/jwks.json\",\n)","handlingStrategy":"validation","validationCode":"if not getattr(verifier, \"jwks_uri\", None):\n    raise RuntimeError(\"JWTVerifier requires a jwks_uri for asymmetric verification\")","typeGuard":"def has_jwks_uri(v) -> bool:\n    return bool(getattr(v, \"jwks_uri\", None))","tryCatchPattern":"try:\n    claims = await verifier.verify_token(token)\nexcept ValueError as e:\n    if \"JWKS URI not configured\" in str(e):\n        # misconfiguration: fix server config, do not retry\n        raise RuntimeError(\"JWT verifier misconfigured: set jwks_uri\") from e\n    raise","preventionTips":["Assert jwks_uri (or a static key) is set when constructing the verifier at startup","Validate config in unit tests: instantiate the verifier from your settings object and check jwks_uri","Fail fast with a health check that hits verify_token with a dummy token at boot"],"tags":["auth","configuration","jwt","jwks"],"backgroundTag":"missing-jwks-uri","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}