The Fix
pip install redis==7.1.0
Based on closed redis/redis-py issue #2551 · PR/commit linked
Production note: Watch p95/p99 latency and retry volume; timeouts can turn into retry storms and duplicate side-effects.
@@ -1,3 +1,4 @@
@@ -1,3 +1,4 @@
+ * Use asyncio.timeout() instead of async_timeout.timeout() for python >= 3.11 (#2602)
* Add test and fix async HiredisParser when reading during a disconnect() (#2349)
* Use hiredis-py pack_command if available.
import async_timeout
class _Timeout(async_timeout.Timeout):
RANDOM_TOKEN = '0f0dd596-373b-42df-aa0b-682d046c5d24'
def __exit__(self, exc_type, exc_val, exc_tb):
self._do_exit(exc_type, exc_val)
return None
async def __aexit__(self, exc_type, exc_val, exc_tb):
self._do_exit(exc_type, exc_val)
return None
def _do_exit(self, exc_type, exc_val):
if exc_type is asyncio.CancelledError and str(exc_val) == _Timeout.RANDOM_TOKEN \
and self._state == async_timeout._State.TIMEOUT:
self._timeout_handler = None
raise asyncio.TimeoutError
# timeout has not expired
self._state = async_timeout._State.EXIT
self._reject()
def _on_timeout(self, task: "asyncio.Task[None]") -> None:
task.cancel(_Timeout.RANDOM_TOKEN)
self._state = async_timeout._State.TIMEOUT
# drop the reference early
self._timeout_handler = None
async_timeout.Timeout = _Timeout
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: [Bug] - CancelledError swallowed (with solution)
- Mechanism: Replaces async_timeout with asyncio.timeout to address issues with asyncio.CancelledError in Redis.
- Why the fix works: Replaces async_timeout with asyncio.timeout to address issues with asyncio.CancelledError in Redis. (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
- Surfaces as: [Bug] - CancelledError swallowed (with solution)
Proof / Evidence
- GitHub issue: #2551
- Fix PR: https://github.com/redis/redis-py/pull/2602
- First fixed release: 7.1.0
- Reproduced locally: No (not executed)
- Last verified: 2026-02-07
- Confidence: 0.85
- Did this fix it?: Yes (upstream fix exists)
- Own content ratio: 0.39
Discussion
High-signal excerpts from the issue thread (symptoms, repros, edge-cases).
“Closing as I assume this issue was fixed with all the async and CancelledError fixes. @svaraborut Feel free to re-open it if this still happen…”
“The fix does only work on py >= 3.11. Will the underlying issue ever be addressed? Currently it is even worse as the patch does…”
Failure Signature (Search String)
- [Bug] - CancelledError swallowed (with solution)
Error Message
Stack trace
Error Message
-------------
[Bug] - CancelledError swallowed (with solution)
Minimal Reproduction
import async_timeout
class _Timeout(async_timeout.Timeout):
RANDOM_TOKEN = '0f0dd596-373b-42df-aa0b-682d046c5d24'
def __exit__(self, exc_type, exc_val, exc_tb):
self._do_exit(exc_type, exc_val)
return None
async def __aexit__(self, exc_type, exc_val, exc_tb):
self._do_exit(exc_type, exc_val)
return None
def _do_exit(self, exc_type, exc_val):
if exc_type is asyncio.CancelledError and str(exc_val) == _Timeout.RANDOM_TOKEN \
and self._state == async_timeout._State.TIMEOUT:
self._timeout_handler = None
raise asyncio.TimeoutError
# timeout has not expired
self._state = async_timeout._State.EXIT
self._reject()
def _on_timeout(self, task: "asyncio.Task[None]") -> None:
task.cancel(_Timeout.RANDOM_TOKEN)
self._state = async_timeout._State.TIMEOUT
# drop the reference early
self._timeout_handler = None
async_timeout.Timeout = _Timeout
What Broke
Tasks may fail to cancel properly, causing unexpected behavior in asynchronous operations.
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/2602
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
- 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 |
|---|---|
| 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.