Jump to solution
Verify

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.

Jump to Verify Open PR/Commit
@@ -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.
repro.py
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
verify
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
fix.md
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).
Production impact:
  • 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…”
@dvora-h · 2023-07-16 · confirmation · source
“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…”
@svaraborut · 2023-11-09 · source

Failure Signature (Search String)

  • [Bug] - CancelledError swallowed (with solution)

Error Message

Stack trace
error.txt
Error Message ------------- [Bug] - CancelledError swallowed (with solution)

Minimal Reproduction

repro.py
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

When NOT to use: Do not use if it changes public behavior or if the failure cannot be reproduced.

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.

Get updates

We publish verified fixes weekly. No spam.

Subscribe

When NOT to Use This Fix

  • Do not use if it changes public behavior or if the failure cannot be reproduced.

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
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.