Skip to content

# Exceptions

Structured API exceptions with consistent error responses and automatic OpenAPI documentation.

## Overview

The `exceptions` module provides a set of pre-built HTTP exceptions and a FastAPI exception handler that formats all errors — including validation errors — into a uniform `ErrorResponse`.

## Setup

Register the exception handlers on your FastAPI app at startup:

    from fastapi import FastAPI
    from fastapi_toolsets.exceptions import init_exceptions_handlers

    app = FastAPI()
    init_exceptions_handlers(app=app)

This registers handlers for:

- `ApiException` — all custom exceptions below
- `RequestValidationError` — Pydantic request validation (422)
- `ResponseValidationError` — Pydantic response validation (422)
- `Exception` — unhandled errors (500)

## Built-in exceptions

Exception | Status | Default message
--- | --- | ---
`UnauthorizedError` | 401 | Unauthorized
`ForbiddenError` | 403 | Forbidden
`NotFoundError` | 404 | Not found
`ConflictError` | 409 | Conflict
`NoSearchableFieldsError` | 400 | No searchable fields
`InvalidFacetFilterError` | 400 | Invalid facet filter

    from fastapi_toolsets.exceptions import NotFoundError

    @router.get("/users/{id}")
    async def get_user(id: int, session: AsyncSession = Depends(get_db)):
        user = await UserCrud.first(session=session, filters=[User.id == id])
        if not user:
            raise NotFoundError
        return user

## Custom exceptions

Subclass `ApiException` and define an `api_error` class variable:

    from fastapi_toolsets.exceptions import ApiException
    from fastapi_toolsets.schemas import ApiError

    class PaymentRequiredError(ApiException):
        api_error = ApiError(
            code=402,
            msg="Payment required",
            desc="Your subscription has expired.",
            err_code="PAYMENT_REQUIRED",
        )

## OpenAPI response documentation

Use `generate_error_responses` to add error schemas to your endpoint's OpenAPI spec:

    from fastapi_toolsets.exceptions import generate_error_responses, NotFoundError, ForbiddenError

    @router.get(
        "/users/{id}",
        responses=generate_error_responses(NotFoundError, ForbiddenError),
    )
    async def get_user(...): ...

Info

The pydantic validation error is automatically added by FastAPI.

:material-api: API Reference