Jump to solution
Verify

The Fix

Fixes the issue where routing to a sub-application added via .add_domain() does not work if the same path exists on the parent app.

Based on closed aio-libs/aiohttp issue #11665 · PR/commit linked

Production note: Most teams hit this during upgrades or environment changes. Roll out with a canary and smoke critical endpoints (health, OpenAPI/docs) before 100%.

Jump to Verify Open PR/Commit
@@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@ +Fixed routing to a sub-application added via ``.add_domain()`` not working +if the same path exists on the parent app. -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py
repro.py
async def test_subapp_domain_routing(aiohttp_client: AiohttpClient) -> None: app = web.Application() subApp = web.Application() class MyViewWrong(web.View): async def get(self) -> web.Response: return web.Response(status=404) class MyViewCorrect(web.View): async def get(self) -> web.Response: return web.Response() app.router.add_routes([web.view("/", MyViewWrong)]) subApp.router.add_routes([web.view("/", MyViewCorrect)]) app.add_domain('different.example.com', subApp) client = await aiohttp_client(app) headers = {'Host': 'different.example.com'} async with client.get("/", headers=headers) as r: assert r.status == 200
verify
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
fix.md
Option A — Apply the official fix\nFixes the issue where routing to a sub-application added via .add_domain() does not work if the same path exists on the parent app.\nWhen NOT to use: This fix is not applicable if the main app should handle the same path as the sub-app.\n\n

Why This Fix Works in Production

  • Trigger: E AssertionError: assert 404 == 200
  • Mechanism: Fixes the issue where routing to a sub-application added via .add_domain() does not work if the same path exists on the parent app.
Production impact:
  • If left unfixed, the same config can fail only in production (env differences), causing startup failures or partial feature outages.

Why This Breaks in Prod

  • Shows up under Python 3.13.7 in real deployments (not just unit tests).
  • Surfaces as: /home/me/aiohttp-1/tests/test_web_urldispatcher.py::test_subapp_domain_routing[pyloop] failed: aiohttp_client = <function aiohttp_client.<locals>.go at 0x775d3d3b56c0>

Proof / Evidence

Discussion

High-signal excerpts from the issue thread (symptoms, repros, edge-cases).

“When tweaking the tests, I noticed that it works correctly if you don't have a handler on the main app. So, the routing is still…”
@Dreamsorcerer · 2025-10-16 · source

Failure Signature (Search String)

  • E AssertionError: assert 404 == 200

Error Message

Stack trace
error.txt
Error Message ------------- /home/me/aiohttp-1/tests/test_web_urldispatcher.py::test_subapp_domain_routing[pyloop] failed: aiohttp_client = <function aiohttp_client.<locals>.go at 0x775d3d3b56c0> async def test_subapp_domain_routing(aiohttp_client: AiohttpClient) -> None: app = web.Application() subApp = web.Application() class MyViewWrong(web.View): async def get(self) -> web.Response: return web.Response(status=404) class MyViewCorrect(web.View): async def get(self) -> web.Response: return web.Response() app.router.add_routes([web.view("/", MyViewWrong)]) subApp.router.add_routes([web.view("/", MyViewCorrect)]) app.add_domain('different.example.com', subApp) client = await aiohttp_client(app) headers = {'Host': 'different.example.com'} async with client.get("/", headers=headers) as r: assert r.status == 200 E AssertionError: assert 404 == 200 E + where 404 = <ClientResponse(http://127.0.0.1:36377/) [404 Not Found]>\n<CIMultiDictProxy('Content-Length': '0', 'Date': 'Thu, 16 Oct 2025 05:35:55 GMT', 'Server': 'Python/3.13 aiohttp/4.0.0a2.dev0')>\n.status MyViewCorrect = <class 'test_web_urldispatcher.test_subapp_domain_routing.<locals>.MyViewCorrect'> MyViewWrong = <class 'test_web_urldispatcher.test_subapp_dom ... (truncated) ...

Minimal Reproduction

repro.py
async def test_subapp_domain_routing(aiohttp_client: AiohttpClient) -> None: app = web.Application() subApp = web.Application() class MyViewWrong(web.View): async def get(self) -> web.Response: return web.Response(status=404) class MyViewCorrect(web.View): async def get(self) -> web.Response: return web.Response() app.router.add_routes([web.view("/", MyViewWrong)]) subApp.router.add_routes([web.view("/", MyViewCorrect)]) app.add_domain('different.example.com', subApp) client = await aiohttp_client(app) headers = {'Host': 'different.example.com'} async with client.get("/", headers=headers) as r: assert r.status == 200

Environment

  • Python: 3.13.7

What Broke

Requests to sub-app domains return 404 instead of the expected response.

Fix Options (Details)

Option A — Apply the official fix

Fixes the issue where routing to a sub-application added via .add_domain() does not work if the same path exists on the parent app.

When NOT to use: This fix is not applicable if the main app should handle the same path as the sub-app.

Fix reference: https://github.com/aio-libs/aiohttp/pull/11673

Last verified: 2026-02-09. Validate in your environment.

Get updates

We publish verified fixes weekly. No spam.

Subscribe

When NOT to Use This Fix

  • This fix is not applicable if the main app should handle the same path as the sub-app.

Verify Fix

verify
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.

Did This Fix Work in Your Case?

Quick signal helps us prioritize which fixes to verify and improve.

Prevention

  • Capture the exact failing error string in logs and tests so you can reproduce via a minimal script.
  • Pin production dependencies and upgrade only with a reproducible test that hits the failing path.

Related Issues

No related fixes found.

Sources

We don’t republish the full GitHub discussion text. Use the links above for context.