PrefectHQ/fastmcp · error · ValueError

No keys found in JWKS

Error message

No keys found in JWKS

What it means

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.

Source

Thrown at fastmcp_slim/fastmcp/server/auth/providers/jwt.py:432

                        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:
            raise ValueError(f"Invalid JWKS JSON: {e}") from e
        except (JoseError, TypeError, KeyError, ValueError) as e:
            self.logger.debug("JWKS key processing failed: %s", e)
            raise ValueError(f"Failed to process JWKS: {e}") from e

    async def _fetch_jwks(self) -> dict[str, Any]:
        """Fetch JWKS data, using SSRF-safe or standard fetch based on config."""
        if not self.jwks_uri:
            raise ValueError("JWKS URI not configured")

        if self.ssrf_safe:

View on GitHub (pinned to 1f02114297)

Solutions

  1. curl your jwks_uri and verify the keys array is non-empty; fix the URL if it points at the wrong realm/tenant
  2. Enable signing keys in your identity provider configuration
  3. Check verifier logs/SSRF settings to ensure fetches aren't being filtered into an empty result
  4. If tokens legitimately carry no kid, provide a single static public key via the verifier's public_key/secret_key option instead of JWKS

Example fix

// before: empty JWKS at wrong URL
verifier = JWTVerifier(jwks_uri="https://idp.example.com/.well-known/jwks.json")
// after: correct realm URL or static key fallback
verifier = JWTVerifier(jwks_uri="https://idp.example.com/realms/myrealm/protocol/openid-connect/certs")
Defensive patterns

Strategy: validation

Validate before calling

import httpx
keys = httpx.get(jwks_uri, timeout=5).json().get("keys", [])
if not keys:
    raise RuntimeError(f"JWKS at {jwks_uri} contains no keys; check IdP signing config")

Type guard

def jwks_has_keys(jwks: dict) -> bool:
    return len(jwks.get("keys", [])) > 0

Try / catch

try:
    claims = await verifier.verify_token(token)
except ValueError as e:
    if "No keys found in JWKS" in str(e):
        raise RuntimeError("IdP publishes no signing keys; fix jwks_uri or IdP config") from e
    raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/4dabb04d63b4e9f1. Report an issue: GitHub.