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

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 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 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:

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:

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:

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 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. The corresponding exception classes live in 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.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →