How Open-Notebook Uses a Typed Exception Hierarchy for Robust Error Handling
Open-Notebook centralizes error handling through a typed exception hierarchy rooted in OpenNotebookError, classifies raw provider failures via classify_error, and converts them to standardized HTTP responses using FastAPI exception handlers.
Open-Notebook implements a robust error management system that transforms unpredictable third-party failures into consistent, user-friendly API responses. The architecture relies on a typed exception hierarchy defined in open_notebook/exceptions.py to categorize errors ranging from database failures to AI provider timeouts. This design ensures that every failure bubbling up from the domain layer or external services is handled predictably and returned to the client with appropriate HTTP status codes and CORS headers.
The Core Exception Hierarchy
All application-specific errors in Open-Notebook inherit from OpenNotebookError, a base class that enables catching any notebook-related failure in a single except clause. This inheritance structure provides type safety and simplifies error propagation across the codebase.
The concrete subclasses cover distinct failure domains:
DatabaseOperationError– Wraps database query or transaction failures raised inopen_notebook/domain/*.pyUnsupportedTypeException– Indicates invalid types supplied to utility helpersInvalidInputError– Signals validation errors for user-provided data, commonly raised in domain models likeopen_notebook/domain/notebook.pyNotFoundError– Indicates requested records do not exist in repository or service layersAuthenticationError– Represents bad credentials or invalid authentication tokens in auth middlewareConfigurationError– Flags misconfigured settings or missing provider models, typically raised inopen_notebook/ai/provision.pyExternalServiceError– Encapsulates remote AI provider errors processed throughopen_notebook/utils/error_classifier.pyRateLimitError– Signifies provider rate-limit violationsFileOperationError– Covers file I/O problems including upload and deletion failuresNetworkError– Represents network-level failures such as timeouts and DNS errorsNoTranscriptFound– Indicates video-to-text extraction failures in podcast workflows
These definitions reside in open_notebook/exceptions.py and serve as the foundation for the error classification pipeline.
Classifying Raw Provider Exceptions
When third-party libraries like Esperanto or LangChain raise native exceptions, Open-Notebook normalizes them through the classify_error utility in open_notebook/utils/error_classifier.py. This function maps unpredictable provider errors to the typed hierarchy using keyword matching.
The classification logic iterates through _CLASSIFICATION_RULES to match exception messages against specific keywords:
# open_notebook/utils/error_classifier.py
def classify_error(exception: BaseException) -> tuple[type[OpenNotebookError], str]:
...
for keywords, exc_class, message in _CLASSIFICATION_RULES:
for keyword in keywords:
if keyword in combined:
user_message = message if message is not None else _truncate(str(exception))
return exc_class, user_message
# fallback → ExternalServiceError
The rule table matches fragments like "authentication", "rate limit", "timeout", or HTTP status codes like "413", converting them into appropriate typed exceptions with user-friendly messages. Unmatched errors default to ExternalServiceError and are logged for monitoring.
Raising Typed Exceptions in Domain Logic
Domain objects and services raise concrete exceptions directly when detecting invalid states or catching low-level failures. This pattern appears throughout the codebase, ensuring that every error bubbles up as a subclass of OpenNotebookError.
For example, in the notebook domain model:
# open_notebook/domain/notebook.py (excerpt)
if not name.strip():
raise InvalidInputError("Notebook name cannot be empty")
...
except SomeDatabaseException as e:
raise DatabaseOperationError(e)
Similar patterns exist in open_notebook/domain/base.py and open_notebook/database/repository.py, where raw database exceptions are caught and re-raised as DatabaseOperationError to maintain abstraction boundaries.
Converting Exceptions to HTTP Responses
The API layer in api/main.py registers dedicated FastAPI exception handlers for each typed exception. These handlers perform three critical functions:
- Set appropriate HTTP status codes (e.g., 400 for
InvalidInputError, 404 forNotFoundError, 401 forAuthenticationError) - Return JSON payloads formatted as
{"detail": "<message>"} - Append CORS headers via the
_cors_headershelper to ensure browser accessibility
# api/main.py (excerpt)
@app.exception_handler(InvalidInputError)
async def invalid_input_error_handler(request: Request, exc: InvalidInputError):
return JSONResponse(
status_code=400,
content={"detail": str(exc)},
headers=_cors_headers(request),
)
A generic handler for OpenNotebookError catches any uncaught subclass and returns a 500 response, preventing unhandled exceptions from leaking stack traces to clients.
End-to-End Error Flow Example
Consider a scenario where a client uploads a file exceeding the AI provider's payload limit:
- Provider layer raises a low-level
HTTPErrorwith message "413 Payload Too Large" classify_errormatches the keyword"413"and returnsExternalServiceErrorwith the message "The request payload is too large..."- The workflow re-raises the
ExternalServiceError - FastAPI catches it via
external_service_error_handler(status 502) and returns a JSON error with CORS headers
# Pseudo-code inside a graph node
try:
await provider.generate(...)
except Exception as exc:
exc_class, msg = classify_error(exc)
raise exc_class(msg) from exc
The resulting HTTP response:
{
"detail": "The request payload is too large for the AI provider. Try reducing the content size or using a different model."
}
Summary
- Open-Notebook defines a centralized exception hierarchy in
open_notebook/exceptions.pywithOpenNotebookErroras the base class and specific subclasses for database, authentication, configuration, and external service failures. - The
classify_errorfunction inopen_notebook/utils/error_classifier.pynormalizes raw third-party exceptions into typed errors using keyword matching rules. - Domain layers raise typed exceptions directly, while repository layers wrap low-level database errors to maintain clean architecture boundaries.
- FastAPI handlers in
api/main.pyconvert each typed exception into appropriate HTTP responses with proper status codes and CORS headers. - This pipeline ensures that unpredictable provider failures become consistent, actionable API errors.
Frequently Asked Questions
What is the base exception class in Open-Notebook?
The base exception class is OpenNotebookError, defined in open_notebook/exceptions.py. All application-specific exceptions inherit from this class, allowing developers to catch any notebook-related error with a single except OpenNotebookError clause while maintaining type safety for specific error handling.
How does Open-Notebook handle errors from third-party AI providers?
Open-Notebook processes raw provider exceptions through the classify_error function in open_notebook/utils/error_classifier.py. This utility matches error messages against keyword rules (e.g., "rate limit", "timeout", "413") and maps them to appropriate typed exceptions like RateLimitError or ExternalServiceError, translating technical failures into user-friendly messages.
Which HTTP status codes does Open-Notebook return for different error types?
The FastAPI exception handlers in api/main.py map specific exceptions to semantic HTTP status codes: InvalidInputError returns 400, NotFoundError returns 404, AuthenticationError returns 401, RateLimitError returns 429, and ExternalServiceError returns 502. A generic handler catches any remaining OpenNotebookError subclasses with status 500.
Where should I add new exception types in the Open-Notebook codebase?
New exception types should be defined in open_notebook/exceptions.py by inheriting from OpenNotebookError. After defining the class, register a corresponding handler in api/main.py that returns the appropriate HTTP status code and CORS headers, following the pattern of existing handlers like invalid_input_error_handler.
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 →