How Open Notebook's error_classifier Maps Raw LLM Exceptions to Typed OpenNotebookError Subclasses

The error_classifier module converts diverse provider-specific exceptions into a unified hierarchy of OpenNotebookError subclasses using keyword-based pattern matching against exception messages and types.

Open Notebook normalizes the chaotic landscape of AI provider errors through a deterministic classification system. The error_classifier module in the lfnovo/open-notebook repository acts as a centralized router that transforms low-level exceptions from Esperanto, LangChain, and other providers into predictable, user-friendly error types. This mapping ensures downstream components handle failures consistently, whether the root cause is an expired API key, network timeout, or rate limit.

The Classification Rules Engine in error_classifier.py

The core logic resides in open_notebook/utils/error_classifier.py [source], specifically within the _CLASSIFICATION_RULES list. This structure defines how raw exceptions map to specific OpenNotebookError subclasses defined in open_notebook/exceptions.py [source].

Each rule in _CLASSIFICATION_RULES is a tuple containing three elements:

  • Keywords: A list of strings searched for in the exception message and type
  • Target subclass: The specific OpenNotebookError subclass to instantiate
  • User message: An optional friendly message; if None, the original exception text is used (truncated)

For example, authentication errors map as follows:

(["authentication", "unauthorized", "invalid api key", "401"],
 AuthenticationError,
 "Authentication failed. Please check your API key in Settings -> Credentials.")

How classify_error Normalizes Exception Data

When the application catches an unhandled exception from an LLM provider, it calls classify_error(exc), which executes a deterministic matching workflow:

  1. String preparation: The function lowercases both the exception's class name and string representation, then concatenates them into a single combined string formatted as "<type>: <message>".

  2. Pattern matching: It iterates through _CLASSIFICATION_RULES, checking if any keyword from each rule appears in the combined string.

  3. First-match wins: The first matching rule determines the exception class. The function returns a tuple of (exception_class, user_message), where user_message is either the custom text from the rule or the original exception text processed through _truncate.

  4. Fallback handling: If no rule matches, the function logs a warning and defaults to ExternalServiceError with a truncated generic message.

Safety Mechanisms and Message Truncation

The module implements defense-in-depth through the _truncate helper function. This utility limits all propagated messages to 200 characters, preventing verbose internal exception details from leaking to users or logs. Whether using the original exception text or a fallback message, the classifier ensures no sensitive stack traces or implementation details exceed this boundary.

Practical Implementation Example

Integrating the classifier into LLM call sites requires wrapping provider-specific execution in a try-except block:

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

def call_llm(provider, payload):
    try:
        # Example provider call – may raise any exception from the LLM lib

        return provider.generate(payload)
    except Exception as exc:                     # Catch any raw provider exception

        exc_cls, message = classify_error(exc)   # Map to OpenNotebookError subclass

        raise exc_cls(message) from exc          # Re‑raise the typed error

# ---- Usage in the application ----

try:
    result = call_llm(my_provider, my_prompt)
except OpenNotebookError as e:
    # All provider‑specific errors are now OpenNotebookError subclasses

    logger.error(f"LLM call failed: {e}")
    # UI can display e.args[0] (the friendly message)

This pattern ensures that:

  • AuthenticationError is raised for messages containing "401", "unauthorized", or "invalid api key"
  • RateLimitError is raised for "429" or "too many requests"
  • NetworkError handles connection failures and timeouts
  • ExternalServiceError catches all remaining provider-specific failures

Summary

  • open_notebook/utils/error_classifier.py contains the _CLASSIFICATION_RULES list and classify_error function that map raw LLM exceptions to typed subclasses.
  • The classifier uses keyword-based pattern matching against a normalized string combining the exception type and message.
  • First-match wins semantics ensure deterministic error classification, with ExternalServiceError serving as the universal fallback.
  • The _truncate function enforces a 200-character limit on all error messages to prevent information leakage.
  • Target exception classes like AuthenticationError, RateLimitError, and NetworkError are defined in open_notebook/exceptions.py.

Frequently Asked Questions

What happens if an LLM exception doesn't match any classification rule?

If classify_error cannot match the exception against any entry in _CLASSIFICATION_RULES, it logs a warning and returns ExternalServiceError along with a truncated generic message. This ensures the application never propagates unclassified raw exceptions.

How does the error_classifier prevent sensitive data from leaking in error messages?

The classifier applies the _truncate function to all messages, hard-limiting them to 200 characters. This safety mechanism prevents verbose stack traces or internal implementation details from reaching user-facing interfaces or logs, regardless of whether the message comes from the original exception or a predefined rule.

Can I customize the error messages shown to users?

Yes. Each entry in _CLASSIFICATION_RULES accepts an optional third parameter for the user-friendly message. If you provide a custom string, that text is used instead of the original exception message. If you pass None, the classifier uses the original exception text (still truncated to 200 characters via _truncate).

Where are the OpenNotebookError subclasses defined?

The typed exception hierarchy—including AuthenticationError, RateLimitError, NetworkError, and ExternalServiceError—is defined in open_notebook/exceptions.py. These classes inherit from the base OpenNotebookError class, allowing downstream code to catch all provider-related failures with a single except clause.

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 →