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
- Verify the URI by opening it in a browser/curl: curl -i $JWKS_URI
- Derive jwks_uri from the IdP's .well-known/openid-configuration instead of hardcoding
- If the failure is transient (5xx/timeouts), retry with backoff and cache the fetched JWKS
- 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
- Cache JWKS with TTL and refresh on kid miss
- Derive jwks_uri from .well-known/openid-configuration
- Add timeouts and circuit breakers around the fetch
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
- ${_fetchRes.status} ${_fetchRes.statusText}
- required "kid" in jwt header: ${JSON.stringify(header)}
- symmetric algorithm "${alg}" is not allowed for JWK verifica
- invalid JWKS response. "keys" field is missing
- invalid JWKS response. "keys" field is not an array
AI-assisted analysis of honojs/hono@e2740d5a1b (2026-08-28).
Data as JSON: /api/errors/e6f1ad9b0b556d2f.
Report an issue: GitHub.