Skip to content

fix(starlette): Stop duplicating scope["root_path"] in URLs#6579

Merged
alexander-alderman-webb merged 22 commits into
masterfrom
webb/asgi/double-mount-prefix
Jun 26, 2026
Merged

fix(starlette): Stop duplicating scope["root_path"] in URLs#6579
alexander-alderman-webb merged 22 commits into
masterfrom
webb/asgi/double-mount-prefix

update django condition

dc3d352
Select commit
Loading
Failed to load commit list.
@sentry/warden / warden completed Jun 23, 2026 in 0s

7 issues

Medium

test_request_url fails on Django < 5.1 due to incorrect root_path setup - `sentry_sdk/integrations/django/asgi.py:103`

The new test_request_url test sets scope["path"] = "/root/nomessage" with scope["root_path"] = "/root" (path already includes root_path), but only skips for Django < 3.0. On Django < 5.1, path_includes_root_path=False causes the URL to be constructed as root_path + path = "/root/root/nomessage", which will fail the assertion == "/root/nomessage". Consider adding @pytest.mark.skipif(django.VERSION < (5, 1), ...) or adding a separate test case for the pre-5.1 path format.

Also found at:

  • tests/integrations/django/asgi/test_asgi.py:1093-1101
Sentry monitoring silently disabled when Quart package version cannot be determined - `sentry_sdk/integrations/quart.py:95-100`

When package_version("quart") returns None (e.g., Quart installed from source without version metadata), the new early-return guard silently skips the SentryAsgiMiddleware entirely, disabling all error capture and tracing for the application. Consider defaulting to path_includes_root_path=False (the pre-spec behavior) instead of bypassing the middleware.

test_request_url doesn't exercise the root_path duplication bug for Starlette - `tests/integrations/starlette/test_starlette.py:1486-1525`

The test uses TestClient with the default root_path="", so the outer ASGI scope always has an empty root_path. The Sentry middleware (guarded by _asgi_middleware_applied) only processes this outer scope, meaning root_path + path and path produce the same URL regardless of whether path_includes_root_path is True or False. The bug (duplication when root_path is non-empty and Starlette >= 0.33 includes it in path) is never triggered. Compare to the Django ASGI test, which correctly sets comm.scope["root_path"] = "/root" with path = "/root/nomessage" to verify no duplication. The Starlette test should use TestClient(starlette_app, root_path="/root") with a request to /nomessage and assert the URL is http://testserver/root/nomessage, not http://testserver/root/root/nomessage.

Sentry monitoring silently disabled when Quart version is undetectable - `sentry_sdk/integrations/quart.py:97-99`

When package_version("quart") returns None (e.g. editable installs or unusual packaging), the or version is None guard causes the patched function to return the raw old_app without any Sentry middleware, silently dropping all transaction tracking and error capturing. The safe fallback is to use path_includes_root_path=False (old behavior) rather than skipping the middleware entirely.

test_request_url fails on Django < 5.1 due to incorrect skipif condition - `tests/integrations/django/asgi/test_asgi.py:1052-1101`

The test passes /root/nomessage as scope["path"] with scope["root_path"] = "/root", but for Django < 5.1 the integration uses path_includes_root_path=False, causing _get_url() to return "/root" + "/root/nomessage" = "/root/root/nomessage", which doesn't match the assertion == "/root/nomessage". The test should be guarded with skipif(django.VERSION < (5, 1)) or use a different path for older Django.

Low

New default `path_includes_root_path=True` changes URL behavior for direct `SentryAsgiMiddleware` users on non-spec-compliant ASGI apps - `sentry_sdk/integrations/asgi.py:120`

The previous behavior always prepended scope["root_path"] to scope["path"] when building URLs (equivalent to path_includes_root_path=False). The new default True assumes path already includes root_path, which is the ASGI-spec-compliant behavior and fixes a duplication bug for compliant apps. However, users who wrap a non-spec-compliant ASGI app (e.g. old Starlette, Starlite, LiteStar, old Quart) directly with SentryAsgiMiddleware — rather than via the bundled integrations — will now silently get URLs that omit the root_path prefix when root_path is non-empty. The bundled integrations pass path_includes_root_path=False for these frameworks, but direct middleware users have no automatic protection and the new parameter is not documented in the __init__ docstring. This is a narrow backwards-compatibility regression (only manifests with a non-empty root_path on a non-compliant app wrapped directly).

Channels < 3.0.0 path missing `path_includes_root_path=False`, causing root_path to be dropped from URLs - `sentry_sdk/integrations/asgi.py:152-157`

In patch_channels_asgi_handler_impl, the SentryAsgiMiddleware created for the channels < 3.0.0 branch does not pass path_includes_root_path, so it inherits the new default of True. For Django < 5.1 (always the case with channels < 3.0.0), scope["path"] does not include scope["root_path"], so path_includes_root_path should be False — matching what patch_django_asgi_handler_impl explicitly does for the same Django version condition. When root_path is non-empty, the URL recorded in Sentry events will be missing the root path prefix.

4 skills analyzed
Skill Findings Duration Cost
security-review 0 12.1s $0.24
code-review 4 6m 29s $3.43
find-bugs 3 17m 25s $5.91
skill-scanner 0 12m 20s $0.15

⏱ 36m 26s · 6.2M in / 267.8k out · $9.73