honojs/hono · error · Error

failed to fetch JWKS from ${options.jwks_uri}

Error message

failed to fetch JWKS from ${options.jwks_uri}

What it means

A plain Error thrown when fetching the JWKS from options.jwks_uri returns a non-OK HTTP response. Without the key set, verification cannot proceed, so the fetch failure surfaces directly.

Source

Thrown at src/utils/jwt/jwt.ts:233

    throw new JwtHeaderRequiresKid(header)
  }

  // Reject symmetric algorithms (HS256, HS384, HS512) to prevent algorithm confusion attacks
  if (symmetricAlgorithms.includes(header.alg as SymmetricAlgorithm)) {
    throw new JwtSymmetricAlgorithmNotAllowed(header.alg)
  }

  // Validate against allowed algorithms
  if (!options.allowedAlgorithms.includes(header.alg as AsymmetricAlgorithm)) {
    throw new JwtAlgorithmNotAllowed(header.alg, options.allowedAlgorithms)
  }

  let verifyKeys = options.keys ? [...options.keys] : undefined

  if (options.jwks_uri) {
    const response = await fetch(options.jwks_uri, init)
    if (!response.ok) {
      throw new Error(`failed to fetch JWKS from ${options.jwks_uri}`)
    }
    const data = (await response.json()) as { keys?: JsonWebKey[] }
    if (!data.keys) {
      throw new Error('invalid JWKS response. "keys" field is missing')
    }
    if (!Array.isArray(data.keys)) {
      throw new Error('invalid JWKS response. "keys" field is not an array')
    }
    verifyKeys ??= []
    verifyKeys.push(...(data.keys as HonoJsonWebKey[]))
  } else if (!verifyKeys) {
    throw new Error('verifyWithJwks requires options for either "keys" or "jwks_uri" or both')
  }

  const matchingKey = verifyKeys.find((key) => key.kid === header.kid)
  if (!matchingKey) {
    throw new JwtTokenInvalid(token)
  }

View on GitHub (pinned to e2740d5a1b)

Solutions

  1. Verify the URI by opening it in a browser/curl: curl -i $JWKS_URI
  2. Derive jwks_uri from the IdP's .well-known/openid-configuration instead of hardcoding
  3. If the failure is transient (5xx/timeouts), retry with backoff and cache the fetched JWKS
  4. Ensure container/network egress allows the IdP domain and check proxy settings

Example fix

// before
const opts = { jwks_uri: 'https://auth.example.com/jwks' } // 404
// after
const opts = { jwks_uri: 'https://auth.example.com/.well-known/jwks.json' }
Defensive patterns

Strategy: retry

Validate before calling

const res = await fetch(jwksUri)
if (!res.ok) throw new Error(`JWKS endpoint unhealthy: ${res.status}`)

Try / catch

try { await verifyWithJwks(token, opts) } catch (e) { if (/failed to fetch JWKS/.test(String(e?.message))) { await cacheJwks.refresh(); return retry() } throw e }

Prevention

When it happens

Trigger: verifyWithJwks with options.jwks_uri set where fetch(jwks_uri, init) yields response.ok === false (404, 401, 500, DNS/proxy returning error pages, etc.).

Common situations: Wrong or outdated jwks_uri (must usually be https://issuer/.well-known/jwks.json); IdP temporarily down; network egress blocked in containers; mTLS/auth headers missing on a protected endpoint; typos in the URI; environment-specific domains.

Related errors


AI-assisted analysis of honojs/hono@e2740d5a1b (2026-08-28). Data as JSON: /api/errors/e6f1ad9b0b556d2f. Report an issue: GitHub.