Skip to main content

Django Explain Errors Middleware

This Django middleware captures errors and exceptions, sends them to OpenAI for explanation, and prints the explanation to stdout when debug mode is enabled. It can optionally ground explanations in your own project source code using a local vector index (RAG), so explanations reference the actual code that failed instead of staying generic.

The middleware supports both synchronous (WSGI) and asynchronous (ASGI) views. It auto-detects the view chain at startup and routes requests through the matching sync or async path, so no extra configuration is required to use it under either server type. Tracebacks are sanitized before leaving the process, and API calls are rate limited. It uses an environment variable to securely manage the OpenAI API key.

Features

  • Captures Django errors and exceptions
  • Uses OpenAI to explain the error
  • Optional codebase-aware explanations (RAG) backed by a local sqlite-vec index (see the RAG section below)
  • Redacts secrets, tokens, and emails from tracebacks before sending
  • Rate limits API calls with a configurable sliding window
  • Works with both sync (WSGI) and async (ASGI) views
  • Securely manages the OpenAI API key using environment variables

Installation

  1. Install django-explain-errors by running:
pip install django-explain-errors
  1. Add the middleware to your Django project:

    • Open your settings.py file and add the middleware to the MIDDLEWARE list. Ensure that the middleware is added last in the list:

      MIDDLEWARE = [
          ...
          'explain_errors.middleware.ExplainErrorsMiddleware',
      ]
      
  2. Set up environment variables:

    • Create a .env file in your project's root directory and add your OpenAI API key. Alternatively, you can set the API key in settings.py:

      OPENAI_API_KEY=your_openai_api_key_here
      

Usage

  1. Ensure DEBUG is set to True:

    Open your settings.py file and set:

    DEBUG = True
    
  2. Trigger an error in your Django application:

    The middleware will capture the error, send it to OpenAI for explanation, and print the explanation to stdout. When an exception is caught, it returns a JSON 500 response containing the error message and the explanation. Set EXPLAIN_ERRORS_PRESERVE_DEBUG_PAGE = True to keep the stdout explanation while letting Django render its standard debug page instead.

Async Support

The middleware exposes both sync_capable = True and async_capable = True. At initialization it inspects get_response to decide whether it is part of a sync or async chain:

  • Under WSGI (for example runserver with sync views), requests flow through the synchronous handler.
  • Under ASGI (for example with async views), requests are awaited through the async handler. The blocking OpenAI call is offloaded with asgiref.sync.sync_to_async so the event loop is not blocked.

No additional settings are needed. Place the middleware last in MIDDLEWARE for both modes.

Configuration

Setting / variable Required Description
OPENAI_API_KEY (env or settings) Yes, when DEBUG=True API key used to authenticate with OpenAI. Read first from the environment, then from settings.
DEBUG Yes The middleware is only active when DEBUG=True. When False, requests pass through untouched.
OPENAI_MODEL No Model used for explanations. Defaults to gpt-4o-mini.
OPENAI_MAX_TOKENS No Maximum tokens in the explanation. Defaults to 150.
OPENAI_TIMEOUT No Request timeout in seconds for the OpenAI client. Defaults to 10.
OPENAI_MAX_TRACEBACK_CHARS No Traceback is trimmed to its last N characters before being sent. Defaults to 3000.
EXPLAIN_ERRORS_PRESERVE_DEBUG_PAGE No When True, the middleware prints the explanation to stdout and re-raises the exception so Django renders its standard debug page instead of a JSON 500. Defaults to False.
OPENAI_BASE_URL (env or settings) No Base URL for any OpenAI-compatible API (for example Ollama at http://localhost:11434/v1). When set, a missing API key is replaced with a placeholder since local servers do not require one.

Using local models (Ollama)

Point OPENAI_BASE_URL at any OpenAI-compatible server to run explanations against a local model instead of the OpenAI API:

OPENAI_BASE_URL = "http://localhost:11434/v1"
OPENAI_MODEL = "llama3.1"
EXPLAIN_ERRORS_RAG_EMBED_MODEL = "nomic-embed-text"

If you use the RAG layer, rebuild the index after changing the embedding model or provider. Stored vectors are model-specific.

Codebase-aware explanations (RAG)

By default, explanations are generated from the traceback alone. With the optional RAG (retrieval-augmented generation) layer enabled, the middleware also retrieves the most relevant chunks of your own project's source code from a local vector index and includes them in the prompt, so explanations can reference your actual functions and classes instead of guessing at them.

This feature is opt-in and adds no dependencies or behavior unless enabled.

Install the extra

pip install django-explain-errors[rag]

This pulls in sqlite-vec, a single-file, no-server vector store. The core package stays dependency-light if you don't need RAG.

Build the index

Add explain_errors to INSTALLED_APPS (needed for Django to discover the management command), then run:

python manage.py build_error_index

This walks your project, chunks Python files by top-level function/class (and other text files by fixed-size line windows), embeds each chunk with the OpenAI embeddings API, and writes them to a local index file. Re-run it whenever your source changes meaningfully — indexing is not automatic. Rebuilding is idempotent: it builds into a temp file and atomically replaces the previous index.

Enable it

# settings.py

EXPLAIN_ERRORS_RAG_ENABLED = True

Settings

Setting Default Description
EXPLAIN_ERRORS_RAG_ENABLED False Master switch for the RAG layer.
EXPLAIN_ERRORS_RAG_INDEX_PATH <BASE_DIR>/.explain_errors_index.db Path to the local vector index file.
EXPLAIN_ERRORS_RAG_TOP_K 4 Number of chunks retrieved and injected into the prompt.
EXPLAIN_ERRORS_RAG_EMBED_MODEL "text-embedding-3-small" OpenAI embedding model used for indexing and retrieval.
EXPLAIN_ERRORS_RAG_INCLUDE None (defaults to BASE_DIR) List of directories to index.
EXPLAIN_ERRORS_RAG_EXCLUDE migrations, venvs, node_modules, static, media, .git Directory names to skip while indexing.
EXPLAIN_ERRORS_RAG_MAX_PROMPT_CHARS 6000 Combined character budget for the traceback + retrieved source sections of the prompt.

Every chunk of source code and every retrieval query is passed through the same sanitize_traceback() redaction used for tracebacks, so secrets in your source files are never sent to OpenAI or written to the index.

If RAG is enabled but the index is missing, sqlite-vec isn't installed, or retrieval fails for any reason, the middleware logs a warning and falls back to the traceback-only prompt — it never breaks error reporting.

RAG-grounded explanations tend to be longer than traceback-only ones. Consider raising OPENAI_MAX_TOKENS (for example to 500) when RAG is enabled so explanations are not truncated.

.gitignore

The index file is a local build artifact, not something to commit. Add it to your project's .gitignore:

.explain_errors_index.db

(Adjust the path if you set EXPLAIN_ERRORS_RAG_INDEX_PATH to something else.)

Before / after

Without RAG — traceback only:

Your ValueError is raised because the value passed to foo() couldn't be converted to an integer. Check where foo() is called and make sure you're passing a numeric string.

With RAG — grounded in the actual function:

In myapp/utils.py, foo() calls int(value) on line 12 without a try/except, so any non-numeric value raises ValueError straight through to the caller. Since foo() is called from myapp/views.py with unvalidated form input, add validation there or wrap the int() call in foo() with a clear error message.

Example

Here is an example of how to use the middleware in a Django project:

# settings.py

DEBUG = True

MIDDLEWARE = [
    ...
    'explain_errors.middleware.ExplainErrorsMiddleware',
]

# .env

OPENAI_API_KEY=your_openai_api_key_here

When an error occurs, you will see an explanation printed to stdout.

License

This project is licensed under the MIT License. See the LICENSE file for details.

Contributing

Contributions are welcome! Please open an issue or submit a pull request for any improvements or bug fixes.

Acknowledgements

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_explain_errors-0.4.0.tar.gz (21.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_explain_errors-0.4.0-py3-none-any.whl (16.8 kB view details)

Uploaded Python 3

File details

Details for the file django_explain_errors-0.4.0.tar.gz.

File metadata

  • Download URL: django_explain_errors-0.4.0.tar.gz
  • Upload date:
  • Size: 21.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for django_explain_errors-0.4.0.tar.gz
Algorithm Hash digest
SHA256 3e6dc0cfd5c1bd0353cfd9001aa9ac534c2d9db8b82e323715e763540bca5e55
MD5 8ceb5ebe1a8f606a5b748d371cd60027
BLAKE2b-256 a0414214d7b15f78f9787f9686caac8114011ed8f09fc81815950d99e798b745

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_explain_errors-0.4.0.tar.gz:

Publisher: publish.yml on topunix/django-explain-errors

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file django_explain_errors-0.4.0-py3-none-any.whl.

File metadata

File hashes

Hashes for django_explain_errors-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 42fecc17e9207774dd4e58d3ad5e54bb29e2e7e87d7b897ac154d8d7801cb20d
MD5 87c0586e02b3537822a9afbf931487b2
BLAKE2b-256 8f390fcd787664b8f1c50887ec66b7ad84f47f32d8a9e24501d69b5b78172669

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_explain_errors-0.4.0-py3-none-any.whl:

Publisher: publish.yml on topunix/django-explain-errors

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 files

0.3.1

2 files

0.3.0

2 files

0.2.0

2 files

0.1

1 file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page