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.
@@ -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
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()
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
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+.
- 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
- GitHub issue: #9360
- Fix PR: https://github.com/aio-libs/aiohttp/pull/9309
- Reproduced locally: No (not executed)
- Last verified: 2026-02-09
- Confidence: 0.95
- Did this fix it?: Yes (upstream fix exists)
- Own content ratio: 0.27
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”
“Appears so, I even looked at this PR and it was hard to see it was fixed! Thank you for pointing this out.”
“In case it makes a difference in how you deploy”
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 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
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+.
Fix reference: https://github.com/aio-libs/aiohttp/pull/9309
Last verified: 2026-02-09. Validate in your environment.
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
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
| Version | Status |
|---|---|
| 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.