honojs/hono · error · Error

verifyWithJwks requires options for either "keys" or "jwks_u

Error message

verifyWithJwks requires options for either "keys" or "jwks_uri" or both

What it means

verifyWithJwks was called with options containing neither a non-empty 'keys' array nor a 'jwks_uri'. The function needs at least one source of verification keys, so it fails fast before doing any work.

Source

Thrown at src/utils/jwt/jwt.ts:245

  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, {
    alg: header.alg,
    ...verifyOpts,
  })
}

View on GitHub (pinned to e2740d5a1b)

Solutions

  1. Pass at least one of options.keys (non-empty HonoJsonWebKey[]) or options.jwks_uri (string)
  2. Check for typos in option names and that dynamic config is not undefined
  3. If keys comes from dynamic filtering, ensure the fallback jwks_uri is included when it ends up empty

Example fix

// before
verifyWithJwks(token, {})
// after
verifyWithJwks(token, { jwks_uri: 'https://auth.example.com/.well-known/jwks.json' })
Defensive patterns

Strategy: validation

Validate before calling

const opts: VerifyJwksOptions = {}
if (jwksUri) opts.jwks_uri = jwksUri
if (keys?.length) opts.keys = keys
if (!opts.jwks_uri && !opts.keys?.length) throw new Error('verification keys not configured')
await verifyWithJwks(token, opts)

Type guard

const hasKeySource = (o: { keys?: unknown[]; jwks_uri?: string }) =>
  !!o.jwks_uri || (Array.isArray(o.keys) && o.keys.length > 0)

Try / catch

try { await verifyWithJwks(token, opts) } catch (e) { if ((e as Error).message.includes('either "keys" or "jwks_uri"')) failStartupConfigCheck(); throw e }

Prevention

When it happens

Trigger: verifyWithJwks(token, {}) or verifyWithJwks(token, { keys: [] }) with no jwks_uri; or a typo'd option name like { jwkUri: ... } or { key: [...] }.

Common situations: Building options dynamically and passing undefined; renaming options during a refactor; passing an empty keys array after filtering and forgetting the URI fallback.

Related errors


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