The Fix
pip install redis==7.1.0
Based on closed redis/redis-py issue #2633 · PR/commit linked
Production note: Watch p95/p99 latency and retry volume; timeouts can turn into retry storms and duplicate side-effects.
@@ -546,6 +546,7 @@ def __del__(
except RuntimeError:
pass
+ self.connection._close()
async def aclose(self, close_connection_pool: Optional[bool] = None) -> None:
from collections.abc import Callable, Awaitable
import redis.asyncio
from fastapi import FastAPI, APIRouter, Request, Response
from httpx import AsyncClient
from starlette.middleware.base import BaseHTTPMiddleware
class DummyMiddleware(BaseHTTPMiddleware):
async def dispatch(
self,
request: Request,
call_next: Callable[[Request], Awaitable[Response]],
) -> Response:
return await call_next(request)
@pytest.mark.asyncio
async def test_redis() -> None:
app = FastAPI()
app.add_middleware(DummyMiddleware)
@app.get("/")
async def index() -> dict[str, str]:
return {}
async with AsyncClient(app=app, base_url="https://example.com/") as client:
await client.get("/")
pool = redis.asyncio.from_url("redis://localhost:6379/0")
await pool.set("key", "value")
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
Option A — Upgrade to fixed release\npip install redis==7.1.0\nWhen NOT to use: Do not use if it changes public behavior or if the failure cannot be reproduced.\n\n
Why This Fix Works in Production
- Trigger: without_httpx
- Mechanism: Fixes the issue of `CancelledError` being raised in asyncio calls by ensuring that the correct timeout library is used for Python versions 3.11.3 and above.
- Why the fix works: Fixes the issue of `CancelledError` being raised in asyncio calls by ensuring that the correct timeout library is used for Python versions 3.11.3 and above. (first fixed release: 7.1.0).
- If left unfixed, tail latency can spike under load and surface as timeouts/retries (amplifying incident impact).
Why This Breaks in Prod
- Triggered by an upgrade/regression window: 3.11.3 breaks; 7.1.0 is the first fixed release.
- Dependency interaction matters here: httpx v0.27.0.
- Shows up under Python 3.11.2 in real deployments (not just unit tests).
- Surfaces as: without_httpx
Proof / Evidence
- GitHub issue: #2633
- Fix PR: https://github.com/redis/redis-py/pull/2999
- First fixed release: 7.1.0
- Affected versions: 3.11.3
- Reproduced locally: No (not executed)
- Last verified: 2026-02-07
- Confidence: 0.75
- Did this fix it?: Yes (upstream fix exists)
- Own content ratio: 0.25
Discussion
High-signal excerpts from the issue thread (symptoms, repros, edge-cases).
“To everyone here, I think I found the culprit”
“@curtiscook Yes, this issue fixed in in 4.5.4 (#2659)”
“> * just closed, then reopen”
“@sileht I have created a minimal reproduction example using httpx and [email protected] Running it will result in: Testing with 4.5.2 also gives the same error,…”
Failure Signature (Search String)
- without_httpx
Error Message
Stack trace
Error Message
-------------
without_httpx
b'1'
b'2'
with_httpx_sync
b'1'
{'args': {}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Host': 'httpbin.org', 'User-Agent': 'python-httpx/0.23.3', 'X-Amzn-Trace-Id': 'Root=1-641c81d6-2e145a8c4ca1ee0a178c2573'}, 'origin': '45.4.34.79', 'url': 'https://httpbin.org/get'}
b'2'
without_httpx_async
b'1'
{'args': {}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Host': 'httpbin.org', 'User-Agent': 'python-httpx/0.23.3', 'X-Amzn-Trace-Id': 'Root=1-641c81d6-3ec88c7b5bd6013d2201d553'}, 'origin': '45.4.34.79', 'url': 'https://httpbin.org/get'}
Traceback (most recent call last):
File "/tmp/xxx/test.py", line 46, in <module>
asyncio.run(main())
File "/home/bellini/.local/share/rtx/installs/python/3.11.2/lib/python3.11/asyncio/runners.py", line 190, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File "/home/bellini/.local/share/rtx/installs/python/3.11.2/lib/python3.11/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/bellini/.local/share/rtx/installs/python/3.11.2/lib/python3.11/asyncio/base_events.py", line 653, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "/tmp/xxx/test.py", line 42, in main
await with_httpx_async()
File "/tmp/xxx/test.py", line 33, in with_httpx_async
.
... (truncated) ...
Stack trace
Error Message
-------------
Using python3.11 (3.11.8)
without_httpx
b'1'
b'2'
with_httpx_async
b'1'
{'args': {}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Host': 'httpbin.org', 'User-Agent': 'python-httpx/0.27.0', 'X-Amzn-Trace-Id': 'Root=1-660b04d4-0ed17ec26d7b388724aaf03b'}, 'origin': '212.85.182.26', 'url': 'https://httpbin.org/get'}
Traceback (most recent call last):
File "/mnt/d/Development/Infold/assembler/asyn_bug.py", line 35, in <module>
asyncio.run(main())
File "/usr/lib/python3.11/asyncio/runners.py", line 188, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/asyncio/runners.py", line 120, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/asyncio/base_events.py", line 650, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "/mnt/d/Development/Infold/assembler/asyn_bug.py", line 31, in main
await with_httpx_async()
File "/mnt/d/Development/Infold/assembler/asyn_bug.py", line 23, in with_httpx_async
print(await conn.get("bar"))
^^^^^^^^^^^^^^^^^^^^^
File "/home/naltharial/.cache/pypoetry/virtualenvs/assembler-01ENdme1-py3.11/lib/python3.11/site-packages/redis/asyncio/client.py", line 605, in execute_command
conn = self.connection or await pool.get_connection(command_name, **options)
... (t
... (truncated) ...
Minimal Reproduction
from collections.abc import Callable, Awaitable
import redis.asyncio
from fastapi import FastAPI, APIRouter, Request, Response
from httpx import AsyncClient
from starlette.middleware.base import BaseHTTPMiddleware
class DummyMiddleware(BaseHTTPMiddleware):
async def dispatch(
self,
request: Request,
call_next: Callable[[Request], Awaitable[Response]],
) -> Response:
return await call_next(request)
@pytest.mark.asyncio
async def test_redis() -> None:
app = FastAPI()
app.add_middleware(DummyMiddleware)
@app.get("/")
async def index() -> dict[str, str]:
return {}
async with AsyncClient(app=app, base_url="https://example.com/") as client:
await client.get("/")
pool = redis.asyncio.from_url("redis://localhost:6379/0")
await pool.set("key", "value")
Environment
- Python: 3.11.2
- httpx: 0.27.0
What Broke
Users experience uncaught CancelledError when writing to Redis using asyncio.
Fix Options (Details)
Option A — Upgrade to fixed release Safe default (recommended)
pip install redis==7.1.0
Use when you can deploy the upstream fix. It is usually lower-risk than long-lived workarounds.
Fix reference: https://github.com/redis/redis-py/pull/2999
First fixed release: 7.1.0
Last verified: 2026-02-07. Validate in your environment.
When NOT to Use This Fix
- Do not use if it changes public behavior or if the failure cannot be reproduced.
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
- Add a stress test that runs high-concurrency workloads and fails on thread dumps / blocked locks.
- Enable watchdog dumps in prod (faulthandler, thread dump endpoint) to capture deadlocks quickly.
- Track RSS + object counts after deployments; alert on monotonic growth and GC pressure.
- Add a long-running test that repeats the failing call path and asserts stable memory.
Version Compatibility Table
| Version | Status |
|---|---|
| 3.11.3 | Broken |
| 7.1.0 | Fixed |
Related Issues
No related fixes found.
Sources
We don’t republish the full GitHub discussion text. Use the links above for context.