Skip to content

Error Handling

Overview

@authrim/server provides a structured error system with 27 error codes across 7 categories. Every error includes metadata for HTTP status, transient classification, and retryability — enabling you to build correct error responses automatically.

AuthrimServerError

All errors thrown by the SDK are instances of AuthrimServerError:

import { AuthrimServerError } from '@authrim/server';
try {
const token = await server.validateToken(accessToken);
} catch (error) {
if (error instanceof AuthrimServerError) {
console.log(error.code); // e.g., 'token_expired'
console.log(error.message); // Human-readable description
console.log(error.meta); // { httpStatus, transient, retryable, wwwAuthenticateError }
}
}

Properties

PropertyTypeDescription
codestringMachine-readable error code (see table below)
messagestringHuman-readable error description
metaAuthrimServerErrorMetaClassification metadata

AuthrimServerErrorMeta

PropertyTypeDescription
httpStatusnumberRecommended HTTP status code for the response
transientbooleanWhether the error is temporary and may resolve on its own
retryablebooleanWhether the operation can be safely retried
wwwAuthenticateErrorstring | undefinedError code for the WWW-Authenticate header (RFC 6750)

Error Code Reference

JWT Validation Errors

CodeHTTP StatusTransientRetryableDescription
invalid_token401NoNoToken is invalid (general validation failure)
token_expired401NoNoToken has expired (exp claim)
token_not_yet_valid401NoNoToken is not yet valid (nbf claim)
token_malformed401NoNoToken cannot be decoded or parsed
signature_invalid401NoNoJWT signature verification failed
algorithm_mismatch401NoNoToken algorithm does not match expected algorithms

Issuer / Audience Errors

CodeHTTP StatusTransientRetryableDescription
invalid_issuer401NoNoToken issuer does not match expected issuer
invalid_audience401NoNoToken audience does not include expected audience

JWKS Errors

CodeHTTP StatusTransientRetryableDescription
jwks_fetch_error502YesYesFailed to fetch the JWKS document
jwks_key_not_found401NoNoNo matching key found in JWKS for the token’s kid
jwks_key_ambiguous401NoNoMultiple keys match the token’s kid
jwks_key_import_error500NoNoFailed to import a JWK for signature verification

DPoP Errors

CodeHTTP StatusTransientRetryableDescription
dpop_proof_missing401NoNoDPoP proof header is missing
dpop_proof_invalid401NoNoDPoP proof is malformed or invalid
dpop_proof_signature_invalid401NoNoDPoP proof signature verification failed
dpop_method_mismatch401NoNoHTTP method in DPoP proof does not match request
dpop_uri_mismatch401NoNoURI in DPoP proof does not match request URL
dpop_ath_mismatch401NoNoAccess token hash in DPoP proof does not match
dpop_binding_mismatch401NoNoDPoP proof key does not match token binding
dpop_iat_expired401NoNoDPoP proof iat is too old
dpop_nonce_required401NoNoServer requires a DPoP nonce but none was provided

Operation Errors

CodeHTTP StatusTransientRetryableDescription
introspection_error502YesYesToken introspection request failed
revocation_error502YesYesToken revocation request failed

Configuration Errors

CodeHTTP StatusTransientRetryableDescription
configuration_error500NoNoInvalid SDK configuration
provider_error500NoNoProvider implementation error

Network Errors

CodeHTTP StatusTransientRetryableDescription
network_error502YesYesNetwork request failed
timeout_error504YesYesRequest timed out

Response Utilities

buildWwwAuthenticateHeader()

Generates an RFC 6750 compliant WWW-Authenticate header value:

import { buildWwwAuthenticateHeader } from '@authrim/server';
const header = buildWwwAuthenticateHeader(error, {
realm: 'my-api',
});
// Example output:
// Bearer realm="my-api", error="invalid_token", error_description="Token has expired"

Options:

OptionTypeDescription
realmstringRealm value for the header

buildErrorResponse()

Generates a JSON error body suitable for HTTP responses:

import { buildErrorResponse } from '@authrim/server';
const body = buildErrorResponse(error);
// {
// error: 'invalid_token',
// error_description: 'Token has expired'
// }

buildErrorHeaders()

Generates response headers including WWW-Authenticate:

import { buildErrorHeaders } from '@authrim/server';
const headers = buildErrorHeaders(error, { realm: 'my-api' });
// {
// 'WWW-Authenticate': 'Bearer realm="my-api", error="invalid_token", ...',
// 'Content-Type': 'application/json'
// }

Error Handling in Middleware

All framework adapters accept an onError callback for logging and monitoring:

import { authrimMiddleware } from '@authrim/server/adapters/express';
app.use(
'/api',
authrimMiddleware(server, {
realm: 'my-api',
onError: (error) => {
// Log to your monitoring service
logger.warn({
code: error.code,
message: error.message,
httpStatus: error.meta.httpStatus,
transient: error.meta.transient,
});
// Track metrics
metrics.increment('auth.error', { code: error.code });
},
}),
);

Framework-Specific Error Patterns

Express

import {
AuthrimServerError,
buildErrorResponse,
buildErrorHeaders,
} from '@authrim/server';
app.get('/api/resource', async (req, res) => {
try {
const token = await server.validateToken(
req.headers.authorization?.replace('Bearer ', '') ?? '',
);
res.json({ data: getResource(token.sub) });
} catch (error) {
if (error instanceof AuthrimServerError) {
const headers = buildErrorHeaders(error, { realm: 'my-api' });
const body = buildErrorResponse(error);
return res.status(error.meta.httpStatus).set(headers).json(body);
}
res.status(500).json({ error: 'internal_error' });
}
});

Hono

import { AuthrimServerError, buildErrorResponse } from '@authrim/server';
app.get('/api/resource', async (c) => {
try {
const token = await server.validateToken(
c.req.header('authorization')?.replace('Bearer ', '') ?? '',
);
return c.json({ data: getResource(token.sub) });
} catch (error) {
if (error instanceof AuthrimServerError) {
return c.json(buildErrorResponse(error), error.meta.httpStatus);
}
return c.json({ error: 'internal_error' }, 500);
}
});

Retry Strategy for Transient Errors

Transient errors (network issues, JWKS fetch failures) can be retried. Use the meta.transient and meta.retryable flags to build retry logic:

import { AuthrimServerError } from '@authrim/server';
async function validateWithRetry(
server: AuthrimServer,
token: string,
maxRetries = 3,
): Promise<ValidatedToken> {
let lastError: AuthrimServerError | undefined;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await server.validateToken(token);
} catch (error) {
if (!(error instanceof AuthrimServerError)) throw error;
lastError = error;
// Only retry transient, retryable errors
if (!error.meta.retryable || attempt === maxRetries) {
throw error;
}
// Exponential backoff: 100ms, 200ms, 400ms
const delay = 100 * Math.pow(2, attempt);
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
throw lastError;
}

Transient vs Non-Transient Summary

CategoryTransientRetryableAction
JWT ValidationNoNoReturn 401 immediately
Issuer/AudienceNoNoReturn 401 immediately
DPoPNoNoReturn 401 immediately
JWKS FetchYesYesRetry with backoff
Operations (introspection/revocation)YesYesRetry with backoff
NetworkYesYesRetry with backoff
ConfigurationNoNoFix configuration and restart

Next Steps