honojs/hono · error · HTTPException
Unauthorized
Error message
Unauthorized
What it means
A token was supplied but verification (signature check against the JWKs, and related JWKS fetch/parse steps) failed, so the middleware throws a 401 HTTPException with error 'invalid_token'. A bare underlying Error (not a subclass) is rethrown as-is; otherwise the HTTPException includes 'token verification failure' in the response body.
Source
Thrown at src/middleware/jwk/jwk.ts:157
let cause
try {
const keys = typeof options.keys === 'function' ? await options.keys(ctx) : options.keys
const jwks_uri =
typeof options.jwks_uri === 'function' ? await options.jwks_uri(ctx) : options.jwks_uri
payload = await Jwt.verifyWithJwks(
token,
{ keys, jwks_uri, verification: verifyOpts, allowedAlgorithms: options.alg },
init
)
} catch (e) {
cause = e
}
if (!payload) {
if (cause instanceof Error && cause.constructor === Error) {
throw cause
}
throw new HTTPException(401, {
message: 'Unauthorized',
res: unauthorizedResponse({
ctx,
error: 'invalid_token',
statusText: 'Unauthorized',
errDescription: 'token verification failure',
realm: options.realm,
}),
cause,
})
}
ctx.set('jwtPayload', payload)
await next()
}
}
View on GitHub (pinned to e2740d5a1b)
Solutions
- Confirm the token's issuer/kid matches a key in the configured JWKS; log the kid header and compare against the fetched key set
- If keys rotated, ensure jwks_uri is used (dynamic fetch) rather than stale static keys
- Verify network access to jwks_uri from the runtime (curl it from the same container) and that it returns valid JSON
- Check that the token itself is intact (not truncated or re-encoded) and, for Node runtimes, that fetch is available for JWKS retrieval
Example fix
// before
app.use(jwk({ keys: oldKeys })) // stale after IdP rotation
// after
app.use(jwk({ jwks_uri: 'https://issuer.example.com/.well-known/jwks.json' })) Defensive patterns
Strategy: try-catch
Validate before calling
import { jwtVerify, createRemoteJWKSet } from 'jose' // optional pre-check
const precheck = async (token: string, jwks: any) => {
try { await jwtVerify(token, jwks); return true } catch { return false }
} Try / catch
try { return await handler(ctx, next) } catch (e) {
if (e instanceof HTTPException && e.status === 401) {
const body = await e.res?.clone().json().catch(() => null)
if (body?.error === 'invalid_token') { /* key mismatch/rotation: log kid, refresh JWKS */ }
}
throw e
} Prevention
- Prefer jwks_uri over static keys so rotation is picked up automatically
- Log the token's kid header on 401s and compare against your JWKS
- Verify jwks_uri is reachable and returns JSON from the deployment environment
- Sync clocks (NTP) on servers validating exp/iat claims
When it happens
Trigger: Token signed with a different key than those in keys/jwks_uri (kid mismatch or rotated keys), a tampered or truncated JWT, an expired/malformed token surfaced as verification failure, or a failure fetching/parsing the JWKS endpoint (network error, invalid JSON).
Common situations: Key rotation on the IdP while the app cached old keys; wrong jwks_uri or environment mismatch (prod token against staging keys); clock skew causing validation errors; proxies stripping Authorization content; JWKS endpoint returning HTML error pages.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- invalid credentials structure
- no authorization included in request
- JWK auth middleware requires options for either "keys" or "j
- invalid credentials structure
- token(${token}) signature mismatched
AI-assisted analysis of honojs/hono@e2740d5a1b (2026-08-28).
Data as JSON: /api/errors/9066baba458c38d6.
Report an issue: GitHub.