How Open Notebook Classifies AI Provider Exceptions Using error_classifier.py
The error_classifier.py utility transforms raw AI provider exceptions into structured, user-friendly error types through a keyword-based mapping system.
The lfnovo/open-notebook repository manages interactions with multiple large language model (LLM) providers through LangChain and Esperanto. To deliver consistent error handling across these integrations, the project implements a centralized classification system in open_notebook/utils/error_classifier.py that converts provider-specific failures into canonical exception types used throughout the application.
The Classification Pipeline
Import of Canonical Exceptions
The module relies on a custom exception hierarchy defined in open_notebook/exceptions.py. All notebook-specific errors inherit from OpenNotebookError, with concrete subclasses including AuthenticationError, RateLimitError, NetworkError, and ExternalServiceError. This hierarchy allows the rest of the codebase to catch and handle errors by category rather than parsing provider-specific strings.
Keyword-Based Classification Rules
At the heart of the system lies the _CLASSIFICATION_RULES list, which maps substring patterns to canonical error classes and user-facing messages:
_CLASSIFICATION_RULES = [
(["authentication", "unauthorized", "invalid api key", "invalid_api_key", "401"],
AuthenticationError,
"Authentication failed. Please check your API key in Settings → Credentials."),
(["rate limit", "rate_limit", "429", "too many requests", "quota exceeded"],
RateLimitError,
"Rate limit exceeded. Please wait a moment and try again."),
# … additional rules …
]
Each tuple contains three elements:
- Keywords: A list of lowercase substrings to match against the exception text or class name
- Exception class: The specific
OpenNotebookErrorsubclass to instantiate - User message: A ready-made string for end users, or
Noneto preserve the original (truncated) message
The classify_error Function
The classify_error function accepts any BaseException and returns a tuple of the appropriate error class and message:
def classify_error(exception: BaseException) -> tuple[type[OpenNotebookError], str]:
error_str = str(exception).lower()
error_type_name = type(exception).__name__.lower()
combined = f"{error_type_name}: {error_str}"
for keywords, exc_class, user_message in _CLASSIFICATION_RULES:
if any(keyword in combined for keyword in keywords):
return exc_class, user_message or _truncate(str(exception))
# Fallback path
return ExternalServiceError, _truncate(str(exception))
The function concatenates the exception type name with its string representation to catch variations like ProviderRateLimitError: 429 Too Many Requests. If no rule matches, it logs a warning and falls back to ExternalServiceError with a generic message containing the truncated original exception. The _truncate helper caps raw messages at 200 characters to prevent accidental exposure of sensitive payload details.
Integration Across LangGraph Workflows
Wrapping LLM Calls in Graph Nodes
Every LangGraph workflow that communicates with LLM providers wraps model invocations with the classifier. In open_notebook/graphs/chat.py, ask.py, source_chat.py, and transformation.py, the pattern follows this structure:
from open_notebook.utils.error_classifier import classify_error
async def invoke_llm(state):
try:
return await model.invoke(state["prompt"])
except Exception as e:
error_class, user_message = classify_error(e)
raise error_class(user_message) from e
This ensures that whether the underlying provider raises an openai.RateLimitError or an anthropic.AuthenticationError, the workflow consistently raises RateLimitError or AuthenticationError from the Open Notebook hierarchy.
API Layer Error Handling
API routers such as api/routers/search.py and api/routers/source_chat.py catch these classified exceptions and forward the user-friendly messages directly to HTTP responses. Because the classifier already normalized the error type, the API layer can handle all provider failures with uniform logic rather than implementing provider-specific parsing in every endpoint.
Summary
- Centralized mapping:
open_notebook/utils/error_classifier.pycontains all provider-to-canonical error translations in one maintainable location - Keyword matching: The system uses substring search against combined exception type names and messages to identify error categories
- Consistent UX: End users receive actionable guidance (e.g., "check your API key") instead of opaque stack traces
- Safety controls: The
_truncatemechanism limits message length to 200 characters, preventing sensitive data leakage - Broad integration: Classification occurs in every LangGraph workflow node and API router that contacts external LLM providers
Frequently Asked Questions
How does error_classifier.py determine which error type to return?
The classify_error function converts the incoming exception to lowercase and combines its type name with its string representation. It iterates through _CLASSIFICATION_RULES and returns the exception class associated with the first rule where any keyword appears in this combined string. This approach catches both explicit error types and message content variations across different AI providers.
What happens when an error doesn't match any classification rule?
If no keywords match, the function logs a warning and returns ExternalServiceError along with a truncated version of the original exception message. This fallback ensures the application never crashes on unrecognized provider errors while still providing some diagnostic information to the user.
Where is the error classification actually invoked in the codebase?
The classifier is invoked in every LangGraph workflow file that contacts LLMs, specifically open_notebook/graphs/chat.py, ask.py, source_chat.py, and transformation.py. Additionally, API routers like api/routers/search.py and api/routers/source_chat.py utilize the classified errors to generate appropriate HTTP responses with user-friendly messages.
Why does the classifier truncate error messages to 200 characters?
The _truncate helper limits raw exception text to 200 characters as a security measure. Provider error messages occasionally contain sensitive information such as API key fragments, internal URLs, or request payloads. By truncating when no specific user message is defined, the system prevents accidental exposure of these details to end users while still preserving enough context for debugging.
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 →