How Error Handling Works in the Open Notebook API: Architecture and Implementation
The Open Notebook API implements centralized error handling through a custom exception hierarchy registered with FastAPI exception handlers that automatically return JSON error responses with proper CORS headers.
The Open Notebook project exposes a FastAPI-based HTTP API that requires robust error handling strategies to deliver consistent client experiences. Instead of exposing raw Python tracebacks, the codebase implements a domain-specific exception hierarchy with centralized handlers that transform errors into standardized JSON payloads. This architecture ensures that every error response—including cross-origin requests—returns appropriate HTTP status codes and descriptive messages while keeping implementation details encapsulated.
Domain-Specific Exception Hierarchy
All API-level errors inherit from the base OpenNotebookError class defined in open_notebook/exceptions.py. This design creates a typed error system that distinguishes between different failure modes without leaking internal implementation details to API consumers.
The hierarchy includes specific exception types for distinct error scenarios:
AuthenticationError– Credential and token validation failures, typically raised in authentication middlewareInvalidInputError– Malformed request data and validation issuesNotFoundError– Missing resources and failed database lookupsConfigurationError– Environment and setup problemsRateLimitError– Request quota exceeded scenariosNetworkErrorandExternalServiceError– Downstream connectivity and third-party service failures
This inheritance structure allows the FastAPI exception handlers to catch specific error types or the generic base class depending on the required granularity of the response.
Centralized Error Handling Implementation
The FastAPI application registers dedicated handlers for each exception type in api/main.py. These handlers intercept raised exceptions and convert them into JSONResponse objects with appropriate HTTP status codes and CORS headers.
Each handler follows a consistent pattern, mapping specific exception types to standard HTTP status codes:
@app.exception_handler(NotFoundError)
async def not_found_error_handler(request: Request, exc: NotFoundError):
return JSONResponse(
status_code=404,
content={"detail": str(exc)},
headers=_cors_headers(request),
)
Similar handlers exist for InvalidInputError (400), AuthenticationError (401), RateLimitError (429), ConfigurationError, NetworkError, ExternalServiceError (typically 500-level), and the generic OpenNotebookError base class as a fallback:
@app.exception_handler(OpenNotebookError)
async def open_notebook_error_handler(request: Request, exc: OpenNotebookError):
return JSONResponse(
status_code=500,
content={"detail": str(exc)},
headers=_cors_headers(request),
)
The system also intercepts FastAPI's built-in HTTPException (Starlette defaults) to apply CORS headers to standard HTTP errors like 413 "Payload Too Large" during file uploads.
CORS-Aware Error Responses
A critical implementation detail involves the _cors_headers(request) helper function defined in api/main.py. Every exception handler invokes this function to inject Access-Control-Allow-* headers into error responses. This prevents browsers from blocking cross-origin error bodies during failed requests, ensuring that client-side JavaScript can read error details even when requests fail preflight or authentication checks.
Without this pattern, browser-based clients would receive opaque network errors for cross-origin failures, making debugging impossible. The Open Notebook implementation ensures CORS compliance for both successful and error responses consistently.
Error Handling Patterns in the Service Layer
Domain logic raises these custom exceptions to signal specific failure states rather than returning None or generic exceptions. In open_notebook/domain/base.py, repository methods raise NotFoundError when database lookups return empty results:
if not record:
raise NotFoundError(f"{table_name} with id {id} not found")
Router modules such as api/routers/chat.py and api/routers/source_chat.py propagate these exceptions directly to the FastAPI handlers rather than catching them locally. The api/auth.py module raises AuthenticationError when middleware detects invalid API tokens:
if not user.is_authorized:
raise AuthenticationError("Invalid API token")
This pattern ensures consistent 401 responses across all protected endpoints without duplicating error response logic in individual routes.
Example usage in an endpoint:
from open_notebook.exceptions import NotFoundError
@router.get("/{notebook_id}")
async def get_notebook(notebook_id: str):
notebook = await notebook_service.get(notebook_id)
if notebook is None:
raise NotFoundError(f"Notebook {notebook_id} not found")
return notebook
Summary
- Domain-specific exceptions: All errors inherit from
OpenNotebookErrorinopen_notebook/exceptions.pyto create a typed error system that distinguishes between authentication, input validation, and resource availability failures. - Centralized handlers:
api/main.pyregisters FastAPI exception handlers using@app.exception_handler()decorators that map exceptions to JSON responses with correct HTTP status codes. - CORS compliance: The
_cors_headers()helper ensures all error responses include necessaryAccess-Control-Allow-*headers for browser clients, preventing opaque cross-origin errors. - Service layer integration: Repository code in
open_notebook/domain/base.pyand authentication logic inapi/auth.pyraise specific exceptions likeNotFoundErrorandAuthenticationErrorto trigger standardized error responses automatically.
Frequently Asked Questions
What is the base exception class used in the Open Notebook API?
The base class is OpenNotebookError, defined in open_notebook/exceptions.py. All domain-specific exceptions such as AuthenticationError and NotFoundError inherit from this class, enabling centralized handling through FastAPI's exception handler system while maintaining type safety throughout the codebase.
How does the API handle CORS headers in error responses?
The API calls _cors_headers(request) within every exception handler in api/main.py to inject Access-Control-Allow-* headers into error responses. This ensures browser clients can read error details during cross-origin requests rather than receiving opaque network errors, which is critical for single-page applications consuming the API.
Where are the FastAPI exception handlers registered?
Exception handlers are registered in api/main.py using the @app.exception_handler() decorator. Each custom exception type—including NotFoundError, InvalidInputError, and AuthenticationError—has a dedicated handler function that returns a JSONResponse with the appropriate HTTP status code and error message.
How should service modules signal that a resource was not found?
Service modules should raise NotFoundError imported from open_notebook.exceptions. Repository code in open_notebook/domain/base.py demonstrates this pattern by raising the exception when database queries return no records, which automatically triggers a 404 response via the registered handler without manual status code management.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →