{"record":{"id":"e6f1ad9b0b556d2f","repo":"honojs/hono","slug":"failed-to-fetch-jwks-from-options-jwks-uri","errorCode":null,"errorMessage":"failed to fetch JWKS from ${options.jwks_uri}","messagePattern":"failed to fetch JWKS from (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/utils/jwt/jwt.ts","lineNumber":233,"sourceCode":"    throw new JwtHeaderRequiresKid(header)\n  }\n\n  // Reject symmetric algorithms (HS256, HS384, HS512) to prevent algorithm confusion attacks\n  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  }","sourceCodeStart":215,"sourceCodeEnd":251,"githubUrl":"https://github.com/honojs/hono/blob/e2740d5a1bd0b4254e517e3af8b60789284bc7bd/src/utils/jwt/jwt.ts#L215-L251","documentation":"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.","triggerScenarios":"verifyWithJwks with options.jwks_uri set where fetch(jwks_uri, init) yields response.ok === false (404, 401, 500, DNS/proxy returning error pages, etc.).","commonSituations":"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.","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"],"exampleFix":"// before\nconst opts = { jwks_uri: 'https://auth.example.com/jwks' } // 404\n// after\nconst opts = { jwks_uri: 'https://auth.example.com/.well-known/jwks.json' }","handlingStrategy":"retry","validationCode":"const res = await fetch(jwksUri)\nif (!res.ok) throw new Error(`JWKS endpoint unhealthy: ${res.status}`)","typeGuard":null,"tryCatchPattern":"try { await verifyWithJwks(token, opts) } catch (e) { if (/failed to fetch JWKS/.test(String(e?.message))) { await cacheJwks.refresh(); return retry() } throw e }","preventionTips":["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"],"tags":["network","jwks","oidc","fetch"],"backgroundTag":"jwks-fetch-failed","analyzedSha":"e2740d5a1bd0b4254e517e3af8b60789284bc7bd","analyzedAt":"2026-08-28T10:18:08.750Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}