honojs/hono · error · Error
invalid JWKS response. "keys" field is not an array
Error message
invalid JWKS response. "keys" field is not an array
What it means
The JWKS response parsed as JSON and contains a 'keys' property, but it is not an array. RFC 7517 requires 'keys' to be an array of JWK objects, so the library rejects the document rather than iterating a non-array value.
Source
Thrown at src/utils/jwt/jwt.ts:240
// 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)
}
// Verify that JWK's alg matches JWT header's alg when JWK has alg field
if (matchingKey.alg && matchingKey.alg !== header.alg) {
throw new JwtAlgorithmMismatch(matchingKey.alg, header.alg)
}
return await verify(token, matchingKey, {View on GitHub (pinned to e2740d5a1b)
Solutions
- curl the JWKS endpoint and check the JSON type of 'keys' — it must be an array
- Fix the server (or mock) to return {"keys":[{...jwk...}, ...]}
- If the endpoint returns a single JWK object, wrap it in an array or pass it via options.keys as [jwk]
Example fix
// before (server)
res.json({ keys: { kty: 'RSA', kid: 'k1', n: '...', e: 'AQAB' } })
// after
res.json({ keys: [{ kty: 'RSA', kid: 'k1', n: '...', e: 'AQAB' }] }) Defensive patterns
Strategy: validation
Validate before calling
const data = await (await fetch(jwksUri)).json()
if (!Array.isArray((data as any)?.keys)) throw new TypeError('JWKS keys must be an array') Type guard
const hasKeyArray = (d: unknown): d is { keys: unknown[] } =>
!!d && typeof d === 'object' && Array.isArray((d as { keys?: unknown }).keys) Try / catch
try { await verifyWithJwks(token, { jwks_uri }) } catch (e) { if (/not an array/.test((e as Error).message)) fixJwksEndpoint(); throw e } Prevention
- Return {"keys":[...]} from custom JWKS endpoints
- Type-check mock JWKS fixtures against a JsonWebKey[] schema
- Validate JWKS shape in a startup health check
When it happens
Trigger: verifyWithJwks({ jwks_uri }) returns JSON where keys is a string, object, or number — e.g. {"keys":"RS256"}, {"keys":{"kty":"RSA"}}, or a custom endpoint returning keys as a map keyed by kid.
Common situations: Hand-rolled /jwks endpoints that return a single JWK object or a map instead of an array; test mocks returning the wrong shape; provider API changes.
Related errors
- required "kid" in jwt header: ${JSON.stringify(header)}
- symmetric algorithm "${alg}" is not allowed for JWK verifica
- invalid JWKS response. "keys" field is missing
- verifyWithJwks requires options for either "keys" or "jwks_u
- JWK auth middleware requires options for either "keys" or "j
AI-assisted analysis of honojs/hono@e2740d5a1b (2026-08-28).
Data as JSON: /api/errors/adb9b588c59f8539.
Report an issue: GitHub.