{"record":{"id":"4dabb04d63b4e9f1","repo":"PrefectHQ/fastmcp","slug":"no-keys-found-in-jwks","errorCode":null,"errorMessage":"No keys found in JWKS","messagePattern":"No keys found in JWKS","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/auth/providers/jwt.py","lineNumber":432,"sourceCode":"                        raise ValueError(\n                            f\"Key ID '{kid}' found in JWKS but its key type \"\n                            \"is unsupported\"\n                        )\n                    self.logger.debug(\n                        \"JWKS key lookup failed: key ID '%s' not found\", kid\n                    )\n                    raise ValueError(f\"Key ID '{kid}' not found in JWKS\")\n                return self._jwks_cache[kid]\n            else:\n                # No kid in token - only allow if there's exactly one key\n                if len(self._jwks_cache) == 1:\n                    return next(iter(self._jwks_cache.values()))\n                elif len(self._jwks_cache) > 1:\n                    raise ValueError(\n                        \"Multiple keys in JWKS but no key ID (kid) in token\"\n                    )\n                else:\n                    raise ValueError(\"No keys found in JWKS\")\n\n        except (SSRFError, SSRFFetchError) as e:\n            self.logger.debug(\"JWKS fetch blocked by SSRF protection: %s\", e)\n            raise ValueError(f\"Failed to fetch JWKS: {e}\") from e\n        except httpx2.HTTPError as e:\n            raise ValueError(f\"Failed to fetch JWKS: {e}\") from e\n        except json.JSONDecodeError as e:\n            raise ValueError(f\"Invalid JWKS JSON: {e}\") from e\n        except (JoseError, TypeError, KeyError, ValueError) as e:\n            self.logger.debug(\"JWKS key processing failed: %s\", e)\n            raise ValueError(f\"Failed to process JWKS: {e}\") from e\n\n    async def _fetch_jwks(self) -> dict[str, Any]:\n        \"\"\"Fetch JWKS data, using SSRF-safe or standard fetch based on config.\"\"\"\n        if not self.jwks_uri:\n            raise ValueError(\"JWKS URI not configured\")\n\n        if self.ssrf_safe:","sourceCodeStart":414,"sourceCodeEnd":450,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/providers/jwt.py#L414-L450","documentation":"The JWKS was fetched successfully (or read from cache) but contained zero keys, so there is no key available to verify any token. The library raises this when the token has no kid and the key set is empty. It signals that the identity provider published no usable keys at that endpoint, or the fetch filtered everything out.","triggerScenarios":"Calling verify_token on a kid-less JWT when _get_jwks_key's cache/fetch yields an empty key set — jwks_uri returns an empty keys array, or all keys were skipped as unsupported.","commonSituations":"Misconfigured jwks_uri pointing at a valid URL with no keys (wrong realm/tenant); IdP with signing disabled; network proxy returning a valid but empty JWKS document; all published keys filtered out due to unsupported key types.","solutions":["curl your jwks_uri and verify the keys array is non-empty; fix the URL if it points at the wrong realm/tenant","Enable signing keys in your identity provider configuration","Check verifier logs/SSRF settings to ensure fetches aren't being filtered into an empty result","If tokens legitimately carry no kid, provide a single static public key via the verifier's public_key/secret_key option instead of JWKS"],"exampleFix":"// before: empty JWKS at wrong URL\nverifier = JWTVerifier(jwks_uri=\"https://idp.example.com/.well-known/jwks.json\")\n// after: correct realm URL or static key fallback\nverifier = JWTVerifier(jwks_uri=\"https://idp.example.com/realms/myrealm/protocol/openid-connect/certs\")\n","handlingStrategy":"validation","validationCode":"import httpx\nkeys = httpx.get(jwks_uri, timeout=5).json().get(\"keys\", [])\nif not keys:\n    raise RuntimeError(f\"JWKS at {jwks_uri} contains no keys; check IdP signing config\")","typeGuard":"def jwks_has_keys(jwks: dict) -> bool:\n    return len(jwks.get(\"keys\", [])) > 0","tryCatchPattern":"try:\n    claims = await verifier.verify_token(token)\nexcept ValueError as e:\n    if \"No keys found in JWKS\" in str(e):\n        raise RuntimeError(\"IdP publishes no signing keys; fix jwks_uri or IdP config\") from e\n    raise","preventionTips":["Curl the jwks_uri during deployment checks and assert a non-empty keys array","Enable signing keys in the IdP and verify via its well-known configuration","Add a startup health check fetching the JWKS","If tokens are kid-less, prefer a single static public key over JWKS lookup"],"tags":["auth","jwt","jwks","configuration"],"backgroundTag":"empty-jwks","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}