Skip to main content

Error model

Every library throw is one of four classes, mapped to a NestJS exception with a redacted payload:

ThrownNest exceptionHTTPPayload highlights
IamUnauthorizedErrorUnauthorizedException401wwwAuthenticate: 'Bearer realm="ory-nestjs"'
IamForbiddenErrorForbiddenException403
IamUpstreamUnavailableErrorServiceUnavailableException503retryAfter: 5
IamConfigurationErrorInternalServerErrorException500Generic message (detail logged server-side only)

Library errors never echo upstream payloads. If a Kratos response contains a JWT or session token, the mapper strips it before the error leaves the library — you cannot accidentally leak PII into an error response or log line.

To surface the wwwAuthenticate hint and retryAfter as real HTTP headers, add a tiny interceptor in your app:

import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
HttpException,
} from '@nestjs/common';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable()
export class IamErrorHeadersInterceptor implements NestInterceptor {
intercept(ctx: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
catchError((err) => {
if (err instanceof HttpException) {
const res = ctx.switchToHttp().getResponse();
const body = err.getResponse() as any;
if (body?.wwwAuthenticate) res.setHeader('WWW-Authenticate', body.wwwAuthenticate);
if (body?.retryAfter != null) res.setHeader('Retry-After', String(body.retryAfter));
}
return throwError(() => err);
}),
);
}
}