# How `classify_error()` Maps Provider-Specific Exceptions to Standard Types in Open Notebook

> Learn how Open Notebook's classify_error function standardizes AI provider exceptions into typed errors using custom rules and a fallback mechanism for predictable error handling.

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

---

**The `classify_error()` function normalizes raw exceptions from AI providers into a predictable hierarchy of Open Notebook exceptions by lowercasing the error class name and message, scanning a keyword-based rule table, and falling back to `ExternalServiceError` when no rule matches.**

`lfnovo/open-notebook` is an open-source project that orchestrates multiple AI providers behind a unified interface. Because each provider—OpenAI, Anthropic, Google, Ollama, and others—throws its own idiosyncratic exceptions, the codebase relies on the `classify_error()` function to shield downstream components from provider-specific noise. This single utility in [`open_notebook/utils/error_classifier.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/error_classifier.py) ensures that graph nodes, API routers, and tests receive typed, actionable errors instead of raw tracebacks.

## The Three-Step Classification Pipeline

`classify_error()` in [`open_notebook/utils/error_classifier.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/error_classifier.py) operates in three distinct phases to transform an arbitrary provider exception into a well-typed Open Notebook error.

### Step 1 — Normalize the Exception String

At lines 82–85, the function begins by flattening the exception into a uniform, lowercased string. It converts both the exception message and its class name to lowercase and concatenates them:

```python
error_str = str(exception).lower()
error_type_name = type(exception).__name__.lower()
combined = f"{error_type_name}: {error_str}"

```

This normalization makes pattern matching immune to capitalization differences and provider-specific phrasing.

### Step 2 — Scan the `_CLASSIFICATION_RULES` Table

Next, the function iterates over `_CLASSIFICATION_RULES`, defined in lines 20–69 of the same file. Each rule is a tuple containing:

- A list of **keywords** to look for in the combined string (for example, `"authentication"` or `"401"`).
- The **target exception class** from `open_notebook.exceptions` (for example, `AuthenticationError`).
- An optional **user-friendly message**. If `None`, the original message is trimmed and returned.

The first matching rule wins. For example, an error containing `"rate limit"` or `"429"` maps to `RateLimitError`, while `"model not found"` maps to `ConfigurationError`.

### Step 3 — Fallback for Unknown Errors

If no rule matches, the classifier logs the error as unclassified and returns the generic `ExternalServiceError` along with a truncated version of the original message, as seen in lines 92–96. This guarantees that users never see raw provider exceptions bubble up to the application surface.

## How Provider Errors Map to Open Notebook Exception Types

The rule table deliberately groups heterogeneous provider patterns into broad, semantically clear categories. According to the source code, the mappings include:

- **Authentication failures** — Keywords such as `"authentication"`, `"401"`, and `"invalid api key"` map to `AuthenticationError` with a default message instructing the user to check their API key.
- **Rate limiting** — Keywords like `"rate limit"`, `"429"`, and `"quota exceeded"` map to `RateLimitError`.
- **Model not found** — Phrases such as `"model not found"` or `"does not exist"` map to `ConfigurationError`, passing through the original message.
- **Network problems** — Keywords including `"connecterror"`, `"timeout"`, and `"connection refused"` map to `NetworkError`.
- **Context-length and token limits** — Patterns like `"context length"` or `"max_tokens"` map to `ExternalServiceError` with a message about oversized content.
- **Payload too large** — Matches on `"413"` or `"payload too large"` yield `ExternalServiceError`.
- **Provider downtime** — Strings containing `"500"` or `"service unavailable"` also map to `ExternalServiceError`, signaling temporary provider unavailability.

## Practical Example: Wrapping a Provider Call

Downstream code in graph nodes and API routers uses `classify_error()` to wrap provider calls safely. Here is how you can handle errors from OpenAI or Ollama:

```python
from open_notebook.utils.error_classifier import classify_error
from open_notebook.exceptions import AuthenticationError, RateLimitError, NetworkError

# Simulated OpenAI authentication failure

try:
    some_openai_call()
except Exception as exc:
    exc_class, user_msg = classify_error(exc)
    assert exc_class is AuthenticationError
    print(user_msg)   # → "Authentication failed. Please check your API key …"

# Simulated Ollama network timeout

try:
    some_ollama_call()
except Exception as exc:
    exc_class, user_msg = classify_error(exc)
    assert exc_class is NetworkError
    print(user_msg)   # → "Could not connect to the AI provider …"

```

Because `classify_error()` returns both the exception class and a user-friendly string, callers can raise the typed error or return it to the UI without parsing provider-specific payloads themselves.

## Key Files in the Error-Translation Layer

Several modules work together to implement this behavior:

- **[`open_notebook/utils/error_classifier.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/error_classifier.py)** — Houses `classify_error()` and the `_CLASSIFICATION_RULES` table.
- **[`open_notebook/exceptions.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/exceptions.py)** — Defines the base exception hierarchy, including `AuthenticationError`, `RateLimitError`, `NetworkError`, and `ExternalServiceError`.
- **[`open_notebook/graphs/source_chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/source_chat.py)** — Example graph node that invokes `classify_error()` to wrap provider errors during chat inference.
- **[`tests/test_embedding.py`](https://github.com/lfnovo/open-notebook/blob/main/tests/test_embedding.py)** — Unit tests that exercise `classify_error()` against mocked provider failures.

## Summary

- **`classify_error()`** is the central utility that converts provider-specific exceptions into a standardized Open Notebook exception hierarchy.
- It normalizes errors by lowercasing the class name and message into a single combined string, enabling case-insensitive matching.
- The **`_CLASSIFICATION_RULES`** table in [`open_notebook/utils/error_classifier.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/error_classifier.py) maps keywords to typed exceptions such as `AuthenticationError`, `RateLimitError`, and `NetworkError`.
- If no rule matches, the function falls back to **`ExternalServiceError`**, ensuring raw provider tracebacks never reach users.
- Graph nodes, API routers, and tests across `lfnovo/open-notebook` depend on this layer for predictable error handling.

## Frequently Asked Questions

### What file contains the `classify_error()` implementation?

The function and its `_CLASSIFICATION_RULES` table are defined in [`open_notebook/utils/error_classifier.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/error_classifier.py). The corresponding exception classes live in [`open_notebook/exceptions.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/exceptions.py).

### Which exceptions can `classify_error()` return?

According to the rule table, it can return `AuthenticationError`, `RateLimitError`, `ConfigurationError`, `NetworkError`, or `ExternalServiceError`. The first match in `_CLASSIFICATION_RULES` determines the result.

### How does `classify_error()` handle unknown provider errors?

When no keyword rule matches, the function logs the error as unclassified and returns `ExternalServiceError` paired with a truncated version of the original message, preventing raw provider exceptions from leaking to the user interface.

### Why lower-case the exception message and class name?

Lowercasing removes case variations between providers—such as `"RateLimitError"` versus `"rate_limit_error"`—so that a single set of keyword rules works consistently across OpenAI, Anthropic, Google, Ollama, and the Esperanto wrapper.