Jump to solution
Verify

The Fix

Fixes the issue where the host header incorrectly included the port number, causing failures in URL building with yarl 1.13.0+.

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

Production note: This usually shows up under retries/timeouts. Treat it as a side-effect risk until you can verify behavior with a canary + real traffic.

Jump to Verify Open PR/Commit
@@ -0,0 +1 @@ @@ -0,0 +1 @@ +Fixed assembling the :class:`~yarl.URL` for web requests when the host contains a non-default port or IPv6 address -- by :user:`bdraco`. diff --git a/aiohttp/web_request.py b/aiohttp/web_request.py index d593a75da74..b7b54fbd41d 100644
repro.py
import asyncio import pytest import subprocess from aiohttp import web async def health_check(request): return web.Response(text="HEALTHY") @web.middleware async def middleware(request, handler): # Access request.url.path to trigger the potential error print(f"Accessing path: {request.url.path}") response = await handler(request) return response async def run_server(): app = web.Application(middlewares=[middleware]) app.router.add_get('/health', health_check) runner = web.AppRunner(app) await runner.setup() site = web.TCPSite(runner, 'localhost', 9090) await site.start() print("Server started on http://localhost:9090") return runner @pytest.mark.asyncio async def test_health_check(): runner = await run_server() try: # Use asyncio.create_subprocess_exec to run curl command proc = await asyncio.create_subprocess_exec( 'curl', '-s', 'http://localhost:9090/health', stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) try: stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=5.0) except asyncio.TimeoutError: print("Curl request timed out") proc.kill() await proc.wait() return if proc.returncode != 0: print(f"Curl failed with return code {proc.returncode}") print(f"stderr: {stderr.decode()}") else: response = stdout.decode().strip() assert response == "HEALTHY", f"Unexpected response: {response}" print("Test passed: Received 'HEALTHY' response") finally: await runner.cleanup()
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 the host header incorrectly included the port number, causing failures in URL building with yarl 1.13.0+.\nWhen NOT to use: This fix should not be applied if the application relies on the incorrect behavior of including port numbers in the host.\n\n

Why This Fix Works in Production

  • Trigger: 10.3.39.66 [30/Sep/2024:11:09:51 -0800] "GET /health HTTP/1.1" 500 246 "-" "kube-probe/1.29"
  • Mechanism: Fixes the issue where the host header incorrectly included the port number, causing failures in URL building with yarl 1.13.0+.
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.11.7 in real deployments (not just unit tests).
  • Surfaces as: 10.3.39.66 [30/Sep/2024:11:09:51 -0800] "GET /health HTTP/1.1" 500 246 "-" "kube-probe/1.29"

Proof / Evidence

Discussion

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

“This looks like a duplicate of https://github.com/aio-libs/aiohttp/issues/9307 which was fixed via https://github.com/aio-libs/aiohttp/pull/9309”
@bdraco · 2024-09-30 · source
“Appears so, I even looked at this PR and it was hard to see it was fixed! Thank you for pointing this out.”
@doctorpangloss · 2024-09-30 · source
“In case it makes a difference in how you deploy”
@bdraco · 2024-09-30 · source

Failure Signature (Search String)

  • 10.3.39.66 [30/Sep/2024:11:09:51 -0800] "GET /health HTTP/1.1" 500 246 "-" "kube-probe/1.29"

Error Message

Stack trace
error.txt
Error Message ------------- 10.3.39.66 [30/Sep/2024:11:09:51 -0800] "GET /health HTTP/1.1" 500 246 "-" "kube-probe/1.29" Error handling request Traceback (most recent call last): File "C:\Python311\Lib\site-packages\aiohttp\web_protocol.py", line 477, in _handle_request resp = await request_handler(request) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Python311\Lib\site-packages\aiohttp\web_app.py", line 559, in _handle return await handler(request) ^^^^^^^^^^^^^^^^^^^^^^ File "C:\Python311\Lib\site-packages\aiohttp\web_middlewares.py", line 117, in impl return await handler(request) ^^^^^^^^^^^^^^^^^^^^^^ File "C:\Python311\Lib\site-packages\comfy\vendor\aiohttp_server_instrumentation.py", line 196, in middleware or _excluded_urls.url_disabled(request.url.path) ^^^^^^^^^^^ File "aiohttp\\_helpers.pyx", line 30, in aiohttp._helpers.reify.__get__ File "C:\Python311\Lib\site-packages\aiohttp\web_request.py", line 457, in url url = URL.build(scheme=self.scheme, host=self.host) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Python311\Lib\site-packages\yarl\_url.py", line 385, in build netloc = cls._make_netloc( ^^^^^^^^^^^^^^^^^ File "C:\Python311\Lib\site-packages\yarl\_url.py", line 1057, in _make_netloc ret = cls._encode_host(host) ^^^^^^^^^^^^^^^^^^^^^^ File ... (truncated) ...

Minimal Reproduction

repro.py
import asyncio import pytest import subprocess from aiohttp import web async def health_check(request): return web.Response(text="HEALTHY") @web.middleware async def middleware(request, handler): # Access request.url.path to trigger the potential error print(f"Accessing path: {request.url.path}") response = await handler(request) return response async def run_server(): app = web.Application(middlewares=[middleware]) app.router.add_get('/health', health_check) runner = web.AppRunner(app) await runner.setup() site = web.TCPSite(runner, 'localhost', 9090) await site.start() print("Server started on http://localhost:9090") return runner @pytest.mark.asyncio async def test_health_check(): runner = await run_server() try: # Use asyncio.create_subprocess_exec to run curl command proc = await asyncio.create_subprocess_exec( 'curl', '-s', 'http://localhost:9090/health', stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) try: stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=5.0) except asyncio.TimeoutError: print("Curl request timed out") proc.kill() await proc.wait() return if proc.returncode != 0: print(f"Curl failed with return code {proc.returncode}") print(f"stderr: {stderr.decode()}") else: response = stdout.decode().strip() assert response == "HEALTHY", f"Unexpected response: {response}" print("Test passed: Received 'HEALTHY' response") finally: await runner.cleanup()

Environment

  • Python: 3.11.7

What Broke

Requests to the aiohttp server resulted in 500 errors due to incorrect URL parsing.

Fix Options (Details)

Option A — Apply the official fix

Fixes the issue where the host header incorrectly included the port number, causing failures in URL building with yarl 1.13.0+.

When NOT to use: This fix should not be applied if the application relies on the incorrect behavior of including port numbers in the host.

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

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 should not be applied if the application relies on the incorrect behavior of including port numbers in the host.

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

  • Make timeouts explicit and test them (unit + integration) to avoid silent behavior changes.
  • Instrument retries (attempt count + reason) and alert on spikes to catch dependency slowdowns.

Version Compatibility Table

VersionStatus
1.13.0 Broken
3.10.6 Broken
3.10.7 Broken

Related Issues

No related fixes found.

Sources

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