-
Notifications
You must be signed in to change notification settings - Fork 45
Description
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