Skip to content

Exception Handling ​

The framework provides a centralized ExceptionHandler that handles reporting (logging) and rendering (HTTP responses) for exceptions across both web and CLI contexts.

Overview ​

ExceptionHandler
├── report()    — logs/tracks the exception
└── render()    — converts the exception into an HTTP response

handle() is the main entry point called by FastAPI. It calls both report() and render() in sequence.

Setup ​

Pass a custom handler class when creating your Application:

python
# bootstrap/application.py
from fastapi_startkit import Application
from fastapi_startkit.exceptions import ExceptionHandler as BaseHandler
from starlette.responses import RedirectResponse

class ExceptionHandler(BaseHandler):
    def register(self):
        self.register_render(
            NotAuthenticated,
            lambda request, exc: RedirectResponse(url="/login", status_code=303),
        )

app = Application(
    base_path=...,
    exception_handler=ExceptionHandler,
)

register() is called automatically during application boot. Wire your renders and reports there.

Registering Renders ​

A render callable converts an exception into a Starlette/FastAPI Response.

python
# Signature: (request, exc) -> Response
def register(self):
    self.register_render(
        PaymentRequiredException,
        lambda request, exc: JSONResponse({"error": str(exc)}, status_code=402),
    )

Renders are matched by exact type. If no render is found for the exception, render() returns None and FastAPI falls back to its own default error handling.

Registering Reports ​

Custom reporters let you send exceptions to external services (Sentry, Datadog, etc.):

python
def register(self):
    self.register_report(
        Exception,
        lambda exc: sentry_sdk.capture_exception(exc),
    )

Suppressing Reports ​

Use dont_report() to skip logging for expected exceptions (e.g. 404s, auth redirects):

python
def register(self):
    self.dont_report([NotAuthenticated, ModelNotFoundException])

Handler Objects ​

For complex logic, register a handler object that implements report(exc) and/or render(request, exc):

python
class MyHandler:
    def report(self, exc):
        sentry_sdk.capture_exception(exc)

    async def render(self, request, exc):
        return JSONResponse({"error": str(exc)}, status_code=500)

def register(self):
    self.register_handler(MyException, MyHandler())

Handler objects support MRO resolution — a handler registered for Exception will also match subclasses unless a more specific handler exists.

How It Wires to FastAPI ​

FastAPIProvider.boot() registers a catch-all exception handler on the FastAPI instance:

python
# Happens automatically — no action needed
self.app.fastapi.add_exception_handler(Exception, handler)

This means every unhandled exception in a request is passed through exception_manager.handle(exc, {"request": request}).

CLI Context ​

In CLI (non-HTTP) contexts, install() wires sys.excepthook to call report() directly. The render path is never reached because there is no HTTP request. KeyboardInterrupt is passed through to the default hook unchanged.

Built-in Exception Types ​

The framework ships several typed exceptions in fastapi_startkit.exceptions:

ExceptionStatus
ModelNotFoundException404
AuthorizationException403
RouteNotFoundException404
MethodNotAllowedException405
ThrottleRequestsException429

These carry get_status(), get_response(), and optionally get_headers() methods that your render callables can use:

python
self.register_render(
    ModelNotFoundException,
    lambda req, exc: JSONResponse(
        {"error": exc.get_response()},
        status_code=exc.get_status(),
    ),
)