Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions sentry-python/5274/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.venv/
__pycache__/
*.pyc
db.sqlite3
*.lock
69 changes: 69 additions & 0 deletions sentry-python/5274/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Reproduction for sentry-python#5274

**Issue:** https://github.com/getsentry/sentry-python/issues/5274

## Description

When running Django under ASGI with `send_default_pii=True`, Sentry's Django integration calls `request.user.is_authenticated` synchronously in the ASGI event processor. On Django 5.x this triggers lazy session-backed user resolution in an async context and raises `SynchronousOnlyOperation`.

## Steps to Reproduce

1. Install dependencies:
```bash
uv sync
```

2. Set up the database and create a superuser:
```bash
uv run python manage.py migrate
uv run python manage.py createsuperuser --username admin --email admin@example.com
```

3. (Optional) Set your Sentry DSN to see events:
```bash
export SENTRY_DSN="your-dsn-here"
```

4. Run the server with uvicorn (ASGI):
```bash
uv run uvicorn asgi:application --host 127.0.0.1 --port 8000
```

5. In a browser:
- Go to http://127.0.0.1:8000/admin/ and log in
- Then visit http://127.0.0.1:8000/

6. Check the console output for the error.

## Expected Behavior

The Sentry SDK should capture errors without raising `SynchronousOnlyOperation`. In async contexts, the Django integration should use async-safe APIs (e.g., `request.auser()`) or skip user capture.

## Actual Behavior

Sentry logs an internal SDK error:

```
SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async.
```

The error occurs in the stack:
```
.../sentry_sdk/integrations/django/__init__.py in is_authenticated
return request_user.is_authenticated
.../sentry_sdk/integrations/django/__init__.py in _set_user_info
if user is None or not is_authenticated(user):
.../sentry_sdk/integrations/django/asgi.py in asgi_request_event_processor
_set_user_info(request, event)
```

## Workaround

Set `send_default_pii=False` and manually call `sentry_sdk.set_user()` after resolving the user asynchronously.

## Environment

- Python: 3.11+
- Django: 5.1+
- sentry-sdk: 2.48.0+
- ASGI server: uvicorn
5 changes: 5 additions & 0 deletions sentry-python/5274/asgi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import os
from django.core.asgi import get_asgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
application = get_asgi_application()
8 changes: 8 additions & 0 deletions sentry-python/5274/manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
13 changes: 13 additions & 0 deletions sentry-python/5274/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[project]
name = "django-asgi-send-default-pii-repro"
version = "0.1.0"
description = "Reproduction for sentry-python#5274"
requires-python = ">=3.11"
dependencies = [
"django>=5.1",
"sentry-sdk>=2.48.0",
"uvicorn",
]

[tool.uv]
dev-dependencies = []
62 changes: 62 additions & 0 deletions sentry-python/5274/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import os
import sentry_sdk

# Initialize Sentry with send_default_pii=True
# This triggers the bug when accessing request.user in async context
sentry_sdk.init(
dsn=os.environ.get("SENTRY_DSN", ""),
send_default_pii=True, # <-- This causes the issue in ASGI
traces_sample_rate=1.0,
)

SECRET_KEY = "insecure-secret-key-for-repro"
DEBUG = True
ALLOWED_HOSTS = ["*"]

INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]

MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
]

ROOT_URLCONF = "urls"

TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]

ASGI_APPLICATION = "asgi.application"

DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": "db.sqlite3",
}
}

STATIC_URL = "/static/"
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
48 changes: 48 additions & 0 deletions sentry-python/5274/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import logging
from django.contrib import admin
from django.urls import path
from django.http import HttpResponse

# Enable debug logging for sentry_sdk to see internal errors
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("sentry_sdk").setLevel(logging.DEBUG)


async def async_view(request):
"""
An async view that triggers Sentry error capture.

When send_default_pii=True, Sentry's Django integration will try to
access request.user.is_authenticated synchronously in the ASGI event
processor, which triggers SynchronousOnlyOperation on Django 5.x.

The error happens in sentry_sdk/integrations/django/asgi.py when
_set_user_info() is called, which accesses request.user.is_authenticated.
"""
print(f"\n{'='*60}")
print(f"Request user type: {type(request.user)}")
print(f"Is async context: True (this is an async view)")
print(f"{'='*60}\n")

# Trigger an error to be captured by Sentry
# The bug manifests when Sentry tries to capture user info
try:
raise ValueError("Test error to trigger Sentry capture")
except ValueError:
import sentry_sdk
# This capture_exception call triggers the ASGI event processor
# which will try to access request.user.is_authenticated synchronously
sentry_sdk.capture_exception()
print("Exception captured by Sentry - check for SynchronousOnlyOperation in logs")

return HttpResponse(
"Check the server console for SynchronousOnlyOperation error from Sentry SDK.\n"
"The error occurs when Sentry tries to access request.user.is_authenticated "
"in the ASGI event processor."
)


urlpatterns = [
path("admin/", admin.site.urls),
path("", async_view),
]