Skip to content

Django middleware doesn't capture exceptions #286

@jamesbrewerdev

Description

@jamesbrewerdev

Bug Report

Summary

posthog.integrations.django.PosthogContextMiddleware doesn't capture exceptions raised by Django views or middlewares. When a Django view or middleware raises an exception, Django converts the exception into a response instead of propagating the exception throw the middleware stack. See the comment in convert_exception_to_response.

Django still makes the exception available to each middleware, but it does so using the Middleware.process_exception function, not Middleware.__call__. See _get_response and _get_response_async.

This isn't caught by the tests in test_middleware.py because the tests use MockRequest which bypasses all of Django code that handles exceptions.

Solution

Add a process_exception function to posthos.integrations.django.PosthogContextMiddleware:

class PosthogContextMiddleware:
    # ...

    def process_exception(self, request, exception):
        if self.request_filter and not self.request_filter(request):
            return

        with contexts.new_context(self.capture_exceptions, client=self.client):
            for k, v in self.extract_tags(request).items():
                contexts.tag(k, v)

            if self.capture_exceptions:
                if self.client:
                    self.client.capture_exception(exception)
                else:
                    contexts.capture_exception(exception)

I copied the implementation from the __call__ function and changed it to handle the exception directly instead of relying on the error handling code in contexts.new_context. It's not clear to me what the downstream effects of re-raising the exception are, so I figure it's better not to.

Environment

Django 5.2.4
PostHog Python SDK 6.1.0
Python 3.13.5

Steps to Reproduce

$ mkdir posthog-django
$ cd posthog-django
$ uv init --name posthog-django --python 3.13.5 .
$ rm main.py
$ uv add django posthog
$ uv run django-admin startproject django_posthog .

Add posthog.integrations.django.PosthogContextMiddleware to MIDDLEWARE in django_posthog/settings.py:

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "posthog.integrations.django.PosthogContextMiddleware",
    # ...
]

Add Posthog settings to the end of django_posthog/settings.py:

from posthog import Posthog

POSTHOG = Posthog(
    "EXAMPLE-API-KEY",
    debug=True,
    enable_exception_autocapture=True,
    host="https://us.i.posthog.com",
)

POSTHOG_MW_CAPTURE_EXCEPTIONS = True
POSTHOG_MW_CLIENT = POSTHOG

Create django_posthog/views.py:

def index(request):
    return 1 / 0

Update django_posthog/urls.py and replace all code with:

from django.urls import path

from django_posthog import views

urlpatterns = [path("", views.index)]

Run uv run python manage.py runserver:

$ uv run python manage.py runserver
DEBUG:posthog:consumer is running...
DEBUG:posthog:consumer is running...
Watching for file changes with StatReloader
INFO:django.utils.autoreload:Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).

You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
July 16, 2025 - 18:52:58
Django version 5.2.4, using settings 'django_posthog.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

WARNING: This is a development server. Do not use it in a production setting. Use a production WSGI or ASGI server instead.
For more information on production servers see: https://docs.djangoproject.com/en/5.2/howto/deployment/

In another terminal, make a request:

curl http://127.0.0.1:8000

In the terminal where you ran uv run python manage.py runserver you could see that a ZeroDivisionError was raised, but there is no indication that the Posthog library handled the exception.

If you make the change I suggested above, restart the server, and retry the request, you should see Posthog log messages like:

DEBUG:posthog:making request: {"batch": [{"properties": {"$current_url": "http://127.0.0.1:8000/" ...
DEBUG:posthog:data uploaded successfully

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions