{"record":{"id":"6cdef706ba315810","repo":"honojs/hono","slug":"invalid-jwks-response-keys-field-is-missing","errorCode":null,"errorMessage":"invalid JWKS response. \"keys\" field is missing","messagePattern":"invalid JWKS response\\. \"keys\" field is missing","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/utils/jwt/jwt.ts","lineNumber":237,"sourceCode":"  if (symmetricAlgorithms.includes(header.alg as SymmetricAlgorithm)) {\n    throw new JwtSymmetricAlgorithmNotAllowed(header.alg)\n  }\n\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)","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/honojs/hono/blob/e2740d5a1bd0b4254e517e3af8b60789284bc7bd/src/utils/jwt/jwt.ts#L219-L255","documentation":"verifyWithJwks fetched the JWKS URI successfully but the parsed JSON body has no 'keys' property. The JWKS format (RFC 7517) requires a top-level 'keys' array, so the library refuses to use the document. This usually means the URL points at something that is not a JWKS endpoint.","triggerScenarios":"Calling verifyWithJwks({ jwks_uri }) where the URI returns valid JSON without a 'keys' field, e.g. an OIDC discovery document, an ID-token endpoint, an HTML-to-JSON error page, or a misrouted API response.","commonSituations":"Using the issuer URL instead of the jwks_uri from the discovery document; auth provider changed its JWKS path; a proxy/gateway returns a JSON error object (e.g. {\"error\":\"not found\"}) with status 200; typo in the JWKS URL.","solutions":["Verify the value of options.jwks_uri is the 'jwks_uri' field from the provider's /.well-known/openid-configuration, not the issuer","curl the JWKS URI and confirm the body is {\"keys\":[...]}","If a proxy intercepts the request, fix routing or add the correct Host/Authorization headers so the real JWKS is returned","Pass keys directly via options.keys as a fallback instead of fetching"],"exampleFix":"// before\nawait verifyWithJwks(token, { jwks_uri: 'https://auth.example.com' })\n// after\nconst disc = await (await fetch('https://auth.example.com/.well-known/openid-configuration')).json()\nawait verifyWithJwks(token, { jwks_uri: disc.jwks_uri })","handlingStrategy":"validation","validationCode":"const res = await fetch(jwksUri)\nconst data = await res.json()\nif (!('keys' in data)) throw new Error('endpoint is not a JWKS document')\nawait verifyWithJwks(token, { keys: data.keys })","typeGuard":"const isJwks = (d: unknown): d is { keys: JsonWebKey[] } =>\n  typeof d === 'object' && d !== null && 'keys' in d && Array.isArray((d as any).keys)","tryCatchPattern":"try { await verifyWithJwks(token, { jwks_uri }) } catch (e) { if (e instanceof Error && e.message.includes('JWKS')) { /* log URI, alert config issue */ } throw e }","preventionTips":["Always source jwks_uri from the OIDC discovery document","Monitor JWKS fetch responses in dev (log status + body shape)","Add integration tests hitting a mocked JWKS with correct shape"],"tags":["jwks","jwt","oauth","config","http"],"backgroundTag":"jwks-fetch-invalid","analyzedSha":"e2740d5a1bd0b4254e517e3af8b60789284bc7bd","analyzedAt":"2026-08-28T10:18:08.750Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}