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

  1. curl the JWKS endpoint and check the JSON type of 'keys' — it must be an array
  2. Fix the server (or mock) to return {"keys":[{...jwk...}, ...]}
  3. 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

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


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