PrefectHQ/fastmcp · error · ValueError
Key ID '{kid}' not found in JWKS
Error message
Key ID '{kid}' not found in JWKS What it means
The token carries a kid whose corresponding key could not be found in the fetched JWKS — either the kid matched no entry, or the only matching entry was skipped. The library raises this so signature verification fails fast instead of attempting verification with the wrong key. It almost always means the token was signed by a key the identity provider no longer publishes (or by a different provider entirely).
Source
Thrown at fastmcp_slim/fastmcp/server/auth/providers/jwt.py:421
self._jwks_cache_time = current_time
# Select the appropriate key
if kid:
if kid not in self._jwks_cache:
if kid in skipped_kids:
self.logger.debug(
"JWKS key lookup failed: key ID '%s' is present "
"but its key type is unsupported",
kid,
)
raise ValueError(
f"Key ID '{kid}' found in JWKS but its key type "
"is unsupported"
)
self.logger.debug(
"JWKS key lookup failed: key ID '%s' not found", kid
)
raise ValueError(f"Key ID '{kid}' not found in JWKS")
return self._jwks_cache[kid]
else:
# No kid in token - only allow if there's exactly one key
if len(self._jwks_cache) == 1:
return next(iter(self._jwks_cache.values()))
elif len(self._jwks_cache) > 1:
raise ValueError(
"Multiple keys in JWKS but no key ID (kid) in token"
)
else:
raise ValueError("No keys found in JWKS")
except (SSRFError, SSRFFetchError) as e:
self.logger.debug("JWKS fetch blocked by SSRF protection: %s", e)
raise ValueError(f"Failed to fetch JWKS: {e}") from e
except httpx2.HTTPError as e:
raise ValueError(f"Failed to fetch JWKS: {e}") from e
except json.JSONDecodeError as e:View on GitHub (pinned to 1f02114297)
Solutions
- Wait for/reduce the JWKS cache TTL (or restart the process) so the verifier re-fetches the current JWKS and picks up the rotated key
- Compare the token's kid (decode the JWT header) against the kids currently published at your jwks_uri to confirm the mismatch
- Confirm jwks_uri points to the correct issuer/realm that actually issued the token
- Re-issue tokens with the current signing key; discard tokens signed by retired keys
Example fix
// before: stale cache after key rotation verifier = JWTVerifier(jwks_uri=..., cache_ttl=3600) // after: shorten TTL so rotated keys are picked up quickly verifier = JWTVerifier(jwks_uri=..., cache_ttl=300)
Defensive patterns
Strategy: retry
Validate before calling
import jwt, httpx
kid = jwt.get_unverified_header(token).get("kid")
published = {k.get("kid") for k in httpx.get(jwks_uri).json()["keys"]}
if kid and kid not in published:
# token signed by an unpublished/retired key
raise RuntimeError(f"kid {kid!r} not in current JWKS") Type guard
def kid_in_jwks(token: str, jwks_keys: list[dict]) -> bool:
kid = jwt.get_unverified_header(token).get("kid")
return kid is not None and any(k.get("kid") == kid for k in jwks_keys) Try / catch
try:
claims = await verifier.verify_token(token)
except ValueError as e:
if "not found in JWKS" in str(e):
# possible key rotation: allow a short retry after cache refresh
await asyncio.sleep(0.5)
claims = await verifier.verify_token(token)
else:
raise Prevention
- Keep the JWKS cache TTL short relative to your IdP's key-rotation cadence
- Monitor IdP key-rotation events and proactively flush the verifier cache
- Verify tokens against the same IdP environment that issued them (avoid staging/prod mixups)
- Double-check jwks_uri points to the issuer's realm
When it happens
Trigger: Calling verify_token on a JWT with a kid that is absent from the JWKS at the configured jwks_uri, including during key rotation before the local JWKS cache (cache TTL) refreshes.
Common situations: IdP rotated signing keys and the verifier's cached JWKS is stale until the TTL expires; token issued by a different environment/tenant (staging token verified against prod IdP); typo in jwks_uri pointing at the wrong realm; tokens minted by a deprecated IdP.
Related errors
- JWKS URI not configured
- Key ID '{kid}' found in JWKS but its key type is unsupported
- Multiple keys in JWKS but no key ID (kid) in token
- No keys found in JWKS
- Failed to extract key ID from token: {e}
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/db161bdd18f374c4.
Report an issue: GitHub.