# How Open Notebook's Error Classification System Translates LLM Provider Exceptions to User-Friendly Messages

> Open Notebook simplifies LLM error handling. Discover how our system translates complex provider exceptions into clear, user-friendly messages, hiding technical details.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: how-to-guide
- Published: 2026-06-19

---

**Open Notebook centralizes LLM error handling through a keyword-based classification system that maps raw provider exceptions to actionable user messages while hiding technical implementation details.**

Open Notebook, an open-source AI note-taking application, handles errors from multiple language model providers through a sophisticated error classification system. Instead of exposing raw API errors to users, the system normalizes exceptions from OpenAI, Anthropic, Groq, and other providers into consistent, actionable feedback. This architecture lives in [`open_notebook/utils/error_classifier.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/error_classifier.py) and integrates deeply with the application's graph-based execution nodes.

## How the Error Classification System Works

### Exception Capture in Graph Nodes

Every graph node that invokes a language model wraps provider calls in try/except blocks. When an exception bubbles up from operations in [`open_notebook/graphs/chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/chat.py), [`open_notebook/graphs/ask.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/ask.py), or similar transformation pipelines, the raw error is immediately passed to the `classify_error` utility. This ensures no provider-specific exception reaches the user interface unprocessed.

### Keyword-Based Mapping Logic

The `classify_error` function in [`open_notebook/utils/error_classifier.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/error_classifier.py) implements a pattern-matching algorithm against the `_CLASSIFICATION_RULES` table. The function constructs a normalized string combining the exception type name and message (`combined = f"{error_type_name}: {error_str}"`), then iterates through classification rules. Each rule specifies:

- A list of trigger keywords (e.g., `"authentication"`, `"401"`, `"invalid api key"`)
- A target exception class from [`open_notebook/exceptions.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/exceptions.py) (e.g., `AuthenticationError`, `RateLimitError`)
- An optional user-friendly message (or `None` to preserve original text)

The first matching rule wins, returning both the mapped exception class and the prepared message.

### Fallback Handling and Logging

When no classification rules match, the system logs a warning and defaults to `ExternalServiceError` with a truncated version of the original message. This prevents information leakage while ensuring the application degrades gracefully for unknown error types.

## The Classification Rules Table

The `_CLASSIFICATION_RULES` table maps common LLM provider error patterns to specific exception types:

- **Authentication errors**: Keywords like `"authentication"`, `"401"`, or `"invalid api key"` map to `AuthenticationError` with the message "Authentication failed. Please check your API key in Settings → Credentials."
- **Rate limiting**: Keywords including `"rate limit"`, `"429"`, or `"too many requests"` trigger `RateLimitError` with "Rate limit exceeded. Please wait a moment and try again."
- **Payload size violations**: `"payload too large"` or `"413"` errors map to `ExternalServiceError` with instructions to reduce content size or switch models.
- **Network failures**: `"connecterror"`, `"timeout"`, or `"connection refused"` generate `NetworkError` messages prompting users to check network connectivity and provider URLs.

## Implementation Examples

Graph nodes implement the classification pattern by wrapping provider calls:

```python
from open_notebook.utils.error_classifier import classify_error
from open_notebook.exceptions import OpenNotebookError

try:
    response = await provider.chat(messages)
except Exception as e:
    # Convert raw provider error → classified error + user message

    error_cls, user_msg = classify_error(e)
    # Raise the mapped OpenNotebookError so the API layer can handle it uniformly

    raise error_cls(user_msg) from e

```

Unit tests validate specific mappings, such as HTTP 413 errors:

```python
from open_notebook.utils.error_classifier import classify_error
from open_notebook.exceptions import ExternalServiceError

exc = Exception("HTTP 413: Payload Too Large")
cls, msg = classify_error(exc)

assert cls is ExternalServiceError
assert "payload is too large" in msg.lower()

```

## Propagation to the API Layer

After classification, the router layer catches the mapped exceptions and returns appropriate HTTP responses containing only the user-friendly text. This design decouples provider-specific error vocabularies from the UI, ensuring that whether an OpenAI rate limit or an Anthropic authentication failure occurs, users receive consistent, actionable guidance without exposure to internal API details.

## Summary

- Open Notebook uses a centralized error classification system in [`open_notebook/utils/error_classifier.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/error_classifier.py) to normalize LLM provider exceptions.
- The `classify_error` function matches combined exception type and message strings against keyword rules in `_CLASSIFICATION_RULES`.
- Mapped exceptions include `AuthenticationError`, `RateLimitError`, `NetworkError`, and `ExternalServiceError` with user-friendly messages.
- Graph nodes in files like [`open_notebook/graphs/chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/chat.py) wrap provider calls to ensure all errors are classified before reaching the API layer.
- Unmatched errors fall back to `ExternalServiceError` with logging to prevent information leakage while maintaining system stability.

## Frequently Asked Questions

### How does Open Notebook handle unknown LLM provider errors?

When the error classification system encounters an exception that matches no predefined rules, it logs a warning and defaults to `ExternalServiceError` with a truncated message. This ensures users receive a generic but safe error notification while developers can investigate the unhandled case through logs.

### Where is the error classification logic implemented in the Open Notebook codebase?

The core logic resides in [`open_notebook/utils/error_classifier.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/error_classifier.py), specifically within the `classify_error` function and the `_CLASSIFICATION_RULES` table. Exception class definitions are located in [`open_notebook/exceptions.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/exceptions.py), while graph implementations in [`open_notebook/graphs/chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/chat.py) and similar files invoke the classifier.

### Can the error classification system distinguish between different types of rate limits?

Yes, the system recognizes rate limiting through multiple keyword patterns including `"rate limit"`, `"429"`, and `"too many requests"`, mapping them all to `RateLimitError` with a consistent user message. This handles variations in how different providers like OpenAI, Anthropic, or Groq report throttling.

### Why does Open Notebook use exception chaining when raising classified errors?

The system uses `raise error_cls(user_msg) from e` to preserve the original exception traceback while replacing the public-facing error. This allows developers to access full provider error details in logs for debugging, while users only see the sanitized, actionable message returned by the API.