Skip to content
ecdsa.com
jwks-rsa (Node.js)JWKS and key discovery

Error message

Unable to find a signing key that matches '2026-10-rotated'

Also printed as

  • error in secret or public key callback: Unable to find a signing key that matches '2026-10-rotated'
  • SigningKeyNotFoundError: Unable to find a signing key that matches
  • No KID specified and JWKS endpoint returned more than 1 key

The quoted value is the token's `kid` (`'undefined'` if the header has none). Through `jwt.verify()` with a callback the text is prefixed with "error in secret or public key callback: ". A token without `kid` against a set with several keys fails with "No KID specified and JWKS endpoint returned more than 1 key".

jwks-rsa (Node.js) — SigningKeyNotFoundError, often wrapped by jsonwebtoken or express-jwt

What it means

jwks-rsa downloaded the key set and none of its usable signing keys has the `kid` from the token header (the value in quotes). The token may be perfectly valid — the verifier simply does not have the key that signed it. When jwks-rsa is used through jsonwebtoken's key callback, the message is prefixed with "error in secret or public key callback:".

Why it happens

How to fix it

  1. 1.

    List the published kids

    Decode the token header and compare its `kid` with the keys at your `jwksUri`. If the `kid` is listed there but the error persists, the key is being filtered out — look at its `use` and `kty`.

    bash
    # kids the provider publishes (jwks-rsa ignores keys with use "enc" and non RSA/EC/OKP keys)
    curl -s https://tenant.example.com/.well-known/jwks.json | jq '.keys[] | {kid, kty, use, alg}'
  2. 2.

    Create one client with cache and rate limiting

    A module-level client with caching refetches the set when a new `kid` appears and protects the provider from a flood of requests when attackers send random `kid` values.

    js
    import jwksRsa from "jwks-rsa";
    
    // One client per process; unknown kids trigger a refetch, bounded by the rate limit.
    export const jwksClient = jwksRsa({
      jwksUri: "https://tenant.example.com/.well-known/jwks.json",
      cache: true,
      cacheMaxAge: 10 * 60 * 1000,
      rateLimit: true,
      jwksRequestsPerMinute: 10,
    });

Related errors

Browse the full signature error database