Skip to main content

Error Handling

Navios provides built-in exception classes for common HTTP errors. Exceptions are automatically converted to appropriate HTTP responses.

Built-in Exceptions​

ExceptionStatus CodeUse Case
BadRequestException400Invalid request data
UnauthorizedException401Missing or invalid authentication
ForbiddenException403Authenticated but not authorized
NotFoundException404Resource not found
ConflictException409Resource conflict (e.g., duplicate)
InternalServerErrorException500Server error

Using Exceptions​

Import and throw exceptions in handlers or services:

import {
BadRequestException,
ConflictException,
NotFoundException,
} from '@navios/core'

@Controller()
class UserController {
private userService = inject(UserService)

@Endpoint(getUser)
async getUser(params: EndpointParams<typeof getUser>) {
const user = await this.userService.findById(params.urlParams.userId)
if (!user) {
throw new NotFoundException('User not found')
}
return user
}

@Endpoint(createUser)
async createUser(params: EndpointParams<typeof createUser>) {
const existing = await this.userService.findByEmail(params.data.email)
if (existing) {
throw new ConflictException('Email already registered')
}
return this.userService.create(params.data)
}
}

Custom Status Codes​

Use HttpException for status codes not covered by built-in exceptions:

import { HttpException } from '@navios/core'

// Too Many Requests
throw new HttpException(429, 'Rate limit exceeded')

// Service Unavailable
throw new HttpException(503, 'Service temporarily unavailable')

// I'm a teapot
throw new HttpException(418, "I'm a teapot")

Error Response Format​

Navios uses RFC 7807 Problem Details format for standardized error responses. This provides a consistent, machine-readable error format across all error types.

HttpException Responses​

HttpException and its subclasses (like NotFoundException, BadRequestException, etc.) maintain backward compatibility and return the original format:

{
"statusCode": 404,
"message": "User not found",
"error": "Not Found"
}

RFC 7807 Problem Details​

For framework-level errors (validation errors, guard rejections, unhandled errors, not found routes), Navios returns RFC 7807 Problem Details:

{
"type": "about:blank",
"title": "Not Found",
"status": 404,
"detail": "Route [GET] /api/users/999 not found"
}

The response includes:

  • type - URI reference identifying the problem type (defaults to "about:blank")
  • title - Short, human-readable summary of the problem
  • status - HTTP status code
  • detail - Human-readable explanation specific to this occurrence
  • errors - (Validation errors only) Structured validation error details

All Problem Details responses include the Content-Type: application/problem+json header.

Validation Errors​

Zod validation errors from endpoint schemas automatically return 400 Bad Request with RFC 7807 Problem Details format:

const createUser = API.declareEndpoint({
method: 'POST',
url: '/users',
requestSchema: z.object({
name: z.string().min(1, 'Name is required'),
email: z.string().email('Invalid email'),
}),
})

// Invalid request { name: "", email: "invalid" }
// Returns: 400 Bad Request with Problem Details

Validation error responses include structured error details:

{
"type": "about:blank",
"title": "Validation Error",
"status": 400,
"detail": "Request validation failed",
"errors": {
"name": {
"code": "too_small",
"minimum": 1,
"type": "string",
"inclusive": true,
"exact": false,
"message": "String must contain at least 1 character(s)",
"path": ["name"]
},
"email": {
"code": "invalid_string",
"validation": "email",
"message": "Invalid email",
"path": ["email"]
}
}
}

Error Handling in Services​

Let exceptions bubble up from services - they'll be caught and converted:

@Injectable()
class UserService {
async findById(id: string) {
const user = await this.db.users.findUnique({ where: { id } })
if (!user) {
throw new NotFoundException(`User ${id} not found`)
}
return user
}

async update(id: string, data: UpdateUserDto) {
const user = await this.findById(id) // Throws if not found
return this.db.users.update({ where: { id }, data })
}
}

Custom Exception Classes​

Create domain-specific exceptions for clearer code:

class UserNotFoundException extends NotFoundException {
constructor(userId: string) {
super(`User ${userId} not found`)
}
}

class EmailAlreadyExistsException extends ConflictException {
constructor(email: string) {
super(`Email ${email} is already registered`)
}
}

// Usage
throw new UserNotFoundException('123')
throw new EmailAlreadyExistsException('[email protected]')

Catching External Errors​

Convert external errors to HTTP exceptions:

@Injectable()
class PaymentService {
async processPayment(amount: number) {
try {
return await this.stripeClient.charge(amount)
} catch (error) {
if (error.code === 'card_declined') {
throw new BadRequestException('Card was declined')
}
throw new InternalServerErrorException('Payment processing failed')
}
}
}

Unhandled Errors​

Unhandled exceptions become 500 Internal Server Error with RFC 7807 Problem Details format:

{
"type": "about:blank",
"title": "Internal Server Error",
"status": 500,
"detail": "An unexpected error occurred"
}

In development, error messages may include more detail. In production, internal details are hidden for security.

Custom Error Responders​

Navios provides a flexible error responder system that you can customize. All error responders implement the ErrorResponder interface and are registered with dependency injection.

Overriding Default Responders​

You can override any default responder by providing a custom implementation with higher priority:

import {
Injectable,
NotFoundResponderToken,
type ErrorResponder,
type ErrorResponse,
} from '@navios/core'

@Injectable({
token: NotFoundResponderToken,
priority: 0, // Higher than default -10
})
export class CustomNotFoundResponder implements ErrorResponder {
getResponse(error: unknown, description?: string): ErrorResponse {
return {
statusCode: 404,
payload: {
type: 'https://api.myapp.com/errors/not-found',
title: 'Resource Not Found',
status: 404,
detail: description ?? 'The requested resource was not found',
},
headers: {
'Content-Type': 'application/problem+json',
},
}
}
}

Available Responder Tokens​

  • NotFoundResponderToken - Handles 404 Not Found errors
  • ForbiddenResponderToken - Handles 403 Forbidden errors (guard rejections)
  • InternalServerErrorResponderToken - Handles 500 Internal Server errors
  • ValidationErrorResponderToken - Handles 400 Validation errors

Using ErrorResponseProducerService​

For custom error handling in adapters or services, you can use ErrorResponseProducerService:

import {
Injectable,
ErrorResponseProducerService,
FrameworkError,
} from '@navios/core'

@Injectable()
export class CustomService {
private errorProducer = inject(ErrorResponseProducerService)

handleError(error: unknown) {
if (error instanceof ZodError) {
return this.errorProducer.respond(
FrameworkError.ValidationError,
error,
)
}
return this.errorProducer.handleUnknown(error)
}
}

The service provides convenience methods:

  • notFound(error, description?) - Produce 404 response
  • forbidden(error, description?) - Produce 403 response
  • internalServerError(error, description?) - Produce 500 response
  • validationError(error, description?) - Produce 400 response
  • handleUnknown(error, description?) - Fallback for unknown errors