Error Handling¶
Arvel standardizes HTTP errors around RFC 9457 Problem Details JSON: type, title, status, optional detail, and instance. That gives browsers, mobile clients, and proxies a predictable shape without sacrificing structured logging on the server. You still raise normal Python exceptions in your domain layer—the framework translates them at the edge.
install_exception_handlers¶
Call install_exception_handlers(app, debug=False) once when building your FastAPI application. It registers handlers for:
- Starlette/FastAPI
HTTPException(the generic one from Starlette) - Arvel’s own
HttpException RequestValidationError(FastAPI/Pydantic validation)- SQLAlchemy
DBAPIError(database driver failures) - A final catch-all for
Exceptionwith full logging
Problem responses use application/problem+json and include the request path in instance.
from fastapi import FastAPI
from arvel.http import install_exception_handlers
app = FastAPI()
install_exception_handlers(app, debug=app.debug)
Arvel HttpException¶
Use HttpException when you want an HTTP status and message without reaching for Starlette’s class directly. It carries status_code and detail, maps cleanly through Arvel’s handler, and participates in the same Problem Details format.
from arvel.http import HttpException
async def must_be_admin(is_admin: bool) -> None:
if not is_admin:
raise HttpException(403, "Insufficient permissions")
Specialized subclasses like ModelNotFoundError and InvalidSignatureError encode common cases with stable messages for APIs and signed URLs.
Domain exception registration¶
register_domain_exception(ExceptionType, status_code) maps your domain errors to HTTP statuses before they bubble to the generic handler. The framework logs context (including ArvelError attributes when present) and returns Problem Details with detail=str(exc).
Built-in mappings include examples like NotFoundError -> 404 and AuthenticationError -> 401; extend the map for your own hierarchy.
from arvel.http.exception_handler import register_domain_exception
from myapp.errors import SeatUnavailableError
register_domain_exception(SeatUnavailableError, 409)
After you register domain types with register_domain_exception, call register_exception(app) once so those mappings become active exception handlers on the FastAPI instance. Order matters: register your custom types before register_exception iterates the map.
Validation errors¶
When FastAPI rejects a body or query, the handler flattens errors into a readable detail string. With debug=True, the Problem payload can include an errors array mirroring FastAPI’s structure—turn this on locally, keep it off in production to avoid leaking input shapes.
Database and unexpected errors¶
Database exceptions log structured fields (request method, URL, request id when available) and return a safe 500 Problem response. Unhandled exceptions log the traceback server-side; only when debug=True does the client see exception names and messages—mirroring Laravel’s APP_DEBUG story.
Debugging tips¶
- Correlate logs with
x-request-idwhen the context middleware populates it; the handler forwards that header on error responses when known. - Prefer raising typed domain errors +
register_domain_exceptionover raw strings in controllers—tests can assert on exception types, and handlers stay thin. - For form validation you own end-to-end, catch
ValidationErrorfrom Arvel’s validator and return your own JSON alongside or instead of FastAPI’s default—just stay consistent with your API’s error contract.