# How the Open Notebook Error Classification System Maps LLM Exceptions to HTTP Responses

> Discover how the Open Notebook error classification system maps LLM exceptions to HTTP responses. Learn about structured error subclasses and FastAPI exception handling for clear API communication.

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

---

**The Open Notebook error classification system converts raw LLM provider exceptions into structured `OpenNotebookError` subclasses through keyword-based rules in [`open_notebook/utils/error_classifier.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/error_classifier.py), then translates those errors into standard HTTP responses via a centralized FastAPI exception handler in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py).**

The `lfnovo/open-notebook` repository centralizes the translation of low-level LLM exceptions into user-friendly HTTP responses. Its error classification system intercepts failures from providers like Esperanto and LangChain, then surfaces them to API consumers as clear messages with precise status codes. This pipeline ensures that authentication errors, rate limits, and network timeouts never leak internal stack traces.

## Keyword-Based Rules in [`open_notebook/utils/error_classifier.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/error_classifier.py)

The core of the error classification system is `_CLASSIFICATION_RULES`, an ordered list defined in [`open_notebook/utils/error_classifier.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/error_classifier.py). Each rule pairs a set of search strings with a concrete subclass of `OpenNotebookError` and an optional human-readable message.

- **Authentication failures:** Keywords like `"authentication"` and `"unauthorized"` map to `AuthenticationError` with a message indicating that authentication failed (lines 20–27).
- **Rate limiting:** Strings like `"rate limit"` and `"429"` trigger `RateLimitError` with a "Rate limit exceeded" explanation (lines 28–33).
- **Network issues:** Terms such as `"connecterror"` and `"timeout"` resolve to `NetworkError`, signaling that the application could not connect to the AI provider (lines 45–50).

Additional rules cover configuration errors, context-length violations, payload-size issues, and generic provider failures.

## How `classify_error` Inspects and Labels Exceptions

When a raw exception bubbles up to the application boundary, routers call `classify_error(exception)` at lines 72–96 of [`error_classifier.py`](https://github.com/lfnovo/open-notebook/blob/main/error_classifier.py). The function lowercases the exception's type name and message, concatenates them into a combined search string, and scans `_CLASSIFICATION_RULES` in order. The first matching rule determines the classified error type.

If a rule matches, `classify_error` returns the corresponding `OpenNotebookError` subclass and a prepared user-friendly message—or a truncated fallback of the original text. When no rule matches, the function logs the anomaly and defaults to a generic `ExternalServiceError`.

## Mapping Classified Errors to HTTP Responses in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py)

The global exception handler registered in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) translates each `OpenNotebookError` subclass into a specific HTTP status code. The handler returns a `JSONResponse` containing the error's detail string.

```python
@app.exception_handler(OpenNotebookError)
async def open_notebook_error_handler(request: Request, exc: OpenNotebookError):
    if isinstance(exc, AuthenticationError):
        status = 401
    elif isinstance(exc, RateLimitError):
        status = 429
    elif isinstance(exc, ConfigurationError):
        status = 400
    elif isinstance(exc, NetworkError):
        status = 502
    elif isinstance(exc, ExternalServiceError):
        status = 502
    else:
        status = 500
    return JSONResponse(status_code=status, content={"detail": str(exc)})

```

This mapping produces the following outcomes:

- `AuthenticationError` → **401 Unauthorized**
- `RateLimitError` → **429 Too Many Requests**
- `ConfigurationError` → **400 Bad Request**
- `NetworkError` → **502 Bad Gateway**
- `ExternalServiceError` → **502 Bad Gateway**
- All other errors → **500 Internal Server Error**

## Router-Level Integration of the Error Classification System

API routes catch broad exceptions and delegate translation to the classification system. In [`api/routers/source_chat.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/source_chat.py) (lines 74–80), a typical endpoint wraps its LLM call like this:

```python
except Exception as e:
    # Translate the raw exception into a classified OpenNotebookError

    error_class, user_message = classify_error(e)
    # FastAPI will route this through the global error handler,

    # which maps the error_class to an appropriate HTTP status.

    raise HTTPException(status_code=500, detail=user_message)

```

Although the router raises an `HTTPException` with `status_code=500`, the global handler in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) intercepts the underlying classified error and overrides the final response with the correct status code and detail.

For example, if a low-level LLM library throws a `TimeoutError`, the `"timeout"` keyword matches the network rule, `classify_error` returns `NetworkError`, and the client receives an HTTP 502 response:

```json
{
  "detail": "Could not connect to the AI provider. Please check your network connection and provider URL."
}

```

## Summary

- **Centralized rules:** [`open_notebook/utils/error_classifier.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/error_classifier.py) defines `_CLASSIFICATION_RULES`, pairing LLM exception keywords with structured `OpenNotebookError` subclasses.
- **Classifier function:** `classify_error()` inspects exception text and returns the first matching error type plus a user-friendly message.
- **HTTP translation:** [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) hosts a global FastAPI exception handler that maps each error subclass to a standard HTTP status code.
- **Router pattern:** API routes in files like [`api/routers/source_chat.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/source_chat.py) call `classify_error()` inside broad `except Exception` blocks to ensure every provider failure surfaces as a clean HTTP response.

## Frequently Asked Questions

### What happens if an LLM exception does not match any classification rule?

`classify_error()` logs the unrecognized exception and returns the generic `ExternalServiceError` subclass. The global FastAPI handler in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) then translates this into an HTTP **502 Bad Gateway** response.

### Which HTTP status code does the system return for rate limit errors?

When the exception text contains keywords like `"rate limit"` or `"429"`, the error classification system produces a `RateLimitError`. The global handler maps this to HTTP **429 Too Many Requests**.

### Can the error classification system handle authentication failures from any LLM provider?

Yes. Because `classify_error()` matches on keyword substrings rather than exact exception types, any provider raising an exception that includes terms like `"authentication"` or `"unauthorized"` is classified as `AuthenticationError` and returned as HTTP **401 Unauthorized**.

### Where is the error hierarchy defined in the Open Notebook codebase?

The `OpenNotebookError` base class and its subclasses—such as `AuthenticationError`, `RateLimitError`, and `NetworkError`—are defined in [`open_notebook/exceptions.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/exceptions.py). The classification rules in [`error_classifier.py`](https://github.com/lfnovo/open-notebook/blob/main/error_classifier.py) and the HTTP handler in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) both depend on this hierarchy.