{"record":{"id":"adb9b588c59f8539","repo":"honojs/hono","slug":"invalid-jwks-response-keys-field-is-not-an-arra","errorCode":null,"errorMessage":"invalid JWKS response. \"keys\" field is not an array","messagePattern":"invalid JWKS response\\. \"keys\" field is not an array","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/utils/jwt/jwt.ts","lineNumber":240,"sourceCode":"\n  // Validate against allowed algorithms\n  if (!options.allowedAlgorithms.includes(header.alg as AsymmetricAlgorithm)) {\n    throw new JwtAlgorithmNotAllowed(header.alg, options.allowedAlgorithms)\n  }\n\n  let verifyKeys = options.keys ? [...options.keys] : undefined\n\n  if (options.jwks_uri) {\n    const response = await fetch(options.jwks_uri, init)\n    if (!response.ok) {\n      throw new Error(`failed to fetch JWKS from ${options.jwks_uri}`)\n    }\n    const data = (await response.json()) as { keys?: JsonWebKey[] }\n    if (!data.keys) {\n      throw new Error('invalid JWKS response. \"keys\" field is missing')\n    }\n    if (!Array.isArray(data.keys)) {\n      throw new Error('invalid JWKS response. \"keys\" field is not an array')\n    }\n    verifyKeys ??= []\n    verifyKeys.push(...(data.keys as HonoJsonWebKey[]))\n  } else if (!verifyKeys) {\n    throw new Error('verifyWithJwks requires options for either \"keys\" or \"jwks_uri\" or both')\n  }\n\n  const matchingKey = verifyKeys.find((key) => key.kid === header.kid)\n  if (!matchingKey) {\n    throw new JwtTokenInvalid(token)\n  }\n\n  // Verify that JWK's alg matches JWT header's alg when JWK has alg field\n  if (matchingKey.alg && matchingKey.alg !== header.alg) {\n    throw new JwtAlgorithmMismatch(matchingKey.alg, header.alg)\n  }\n\n  return await verify(token, matchingKey, {","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/honojs/hono/blob/e2740d5a1bd0b4254e517e3af8b60789284bc7bd/src/utils/jwt/jwt.ts#L222-L258","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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]"],"exampleFix":"// before (server)\nres.json({ keys: { kty: 'RSA', kid: 'k1', n: '...', e: 'AQAB' } })\n// after\nres.json({ keys: [{ kty: 'RSA', kid: 'k1', n: '...', e: 'AQAB' }] })","handlingStrategy":"validation","validationCode":"const data = await (await fetch(jwksUri)).json()\nif (!Array.isArray((data as any)?.keys)) throw new TypeError('JWKS keys must be an array')","typeGuard":"const hasKeyArray = (d: unknown): d is { keys: unknown[] } =>\n  !!d && typeof d === 'object' && Array.isArray((d as { keys?: unknown }).keys)","tryCatchPattern":"try { await verifyWithJwks(token, { jwks_uri }) } catch (e) { if (/not an array/.test((e as Error).message)) fixJwksEndpoint(); throw e }","preventionTips":["Return {\"keys\":[...]} from custom JWKS endpoints","Type-check mock JWKS fixtures against a JsonWebKey[] schema","Validate JWKS shape in a startup health check"],"tags":["jwks","jwt","schema","json"],"backgroundTag":"jwks-fetch-invalid","analyzedSha":"e2740d5a1bd0b4254e517e3af8b60789284bc7bd","analyzedAt":"2026-08-28T10:18:08.750Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}