Jump to solution
Verify

The Fix

pip install redis==4.0.0

Based on closed redis/redis-py issue #764 · PR/commit linked

Production note: This tends to surface only under concurrency. Reproduce with load tests and watch for lock contention/cancellation paths.

Jump to Verify Open PR/Commit
@@ -2903,7 +2903,9 @@ def reset(self): self.connection = None self.channels = {} + self.pending_unsubscribe_channels = set() self.patterns = {} + self.pending_unsubscribe_patterns = set()
repro.py
def test_sub_unsub_sub(): # redis since 2.10.0 to newest (3.0.1) import redis cli = redis.Redis() pubsub = cli.pubsub() pubsub.subscribe('test-chan') pubsub.unsubscribe('test-chan') pubsub.subscribe('test-chan') assert pubsub.subscribed message = pubsub.get_message() assert message assert message['channel'] == 'test-chan', message assert message['type'] == 'subscribe', message message = pubsub.get_message() assert message assert message['channel'] == 'test-chan', message assert message['type'] == 'unsubscribe', message message = pubsub.get_message() assert message assert message['channel'] == 'test-chan', message assert message['type'] == 'subscribe', message assert pubsub.subscribed # <-- this fails! if __name__ == '__main__': test_sub_unsub_sub()
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==4.0.0\nWhen NOT to use: This fix is not suitable if the application requires strict message delivery guarantees.\n\n

Why This Fix Works in Production

  • Trigger: We recently came across some strange behaviour in our web servers, and tracked the problem down to a race condition in redispy, which is triggered when you…
  • Mechanism: A race condition occurs when unsubscribing and quickly resubscribing to a channel
  • Why the fix works: Fixes a race condition in redispy that causes it to forget the channel when unsubscribing and quickly resubscribing. (first fixed release: 4.0.0).
Production impact:
  • If left unfixed, failures can be intermittent under concurrency (hard to reproduce; shows up as sporadic 5xx/timeouts).

Why This Breaks in Prod

  • A race condition occurs when unsubscribing and quickly resubscribing to a channel
  • Production symptom (often without a traceback): We recently came across some strange behaviour in our web servers, and tracked the problem down to a race condition in redispy, which is triggered when you unsubscribe and quickly resubscribe to a channel.

Proof / Evidence

  • GitHub issue: #764
  • Fix PR: https://github.com/redis/redis-py/pull/1105
  • First fixed release: 4.0.0
  • Reproduced locally: No (not executed)
  • Last verified: 2026-02-08
  • Confidence: 0.85
  • Did this fix it?: Yes (upstream fix exists)
  • Own content ratio: 0.45

Discussion

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

“I wrote a simple test function which shows a problem. Problem is since 2.10.0 version.”
@marc1n · 2018-12-04 · source
“The ugly consequence of this bug is that in case of reconnect, our channel will be not re-subscribed (because self.channels does not have it)!”
@marc1n · 2018-12-04 · source
“Possible fix (non breaking current semantic of unsubscribe) is to add pending_unsubscribe set which keeps channels to be unsubscribed:”
@marc1n · 2018-12-05 · source
“Is there any contributor, here? I could prepare a pull request, but I see a lot of pull requests (78) nobody cares of - it…”
@marc1n · 2018-12-17 · source

Failure Signature (Search String)

  • We recently came across some strange behaviour in our web servers, and tracked the problem down to a race condition in redispy, which is triggered when you unsubscribe and quickly
  • pubsub.subscribe() synchronously adds the channel to pubsub.channels, however pubsub.unsubscribe() doesn't: it waits for the redis server to asynchronously send an unsubscribe
Copy-friendly signature
signature.txt
Failure Signature ----------------- We recently came across some strange behaviour in our web servers, and tracked the problem down to a race condition in redispy, which is triggered when you unsubscribe and quickly resubscribe to a channel. pubsub.subscribe() synchronously adds the channel to pubsub.channels, however pubsub.unsubscribe() doesn't: it waits for the redis server to asynchronously send an unsubscribe message.

Error Message

Signature-only (no traceback captured)
error.txt
Error Message ------------- We recently came across some strange behaviour in our web servers, and tracked the problem down to a race condition in redispy, which is triggered when you unsubscribe and quickly resubscribe to a channel. pubsub.subscribe() synchronously adds the channel to pubsub.channels, however pubsub.unsubscribe() doesn't: it waits for the redis server to asynchronously send an unsubscribe message.

Minimal Reproduction

repro.py
def test_sub_unsub_sub(): # redis since 2.10.0 to newest (3.0.1) import redis cli = redis.Redis() pubsub = cli.pubsub() pubsub.subscribe('test-chan') pubsub.unsubscribe('test-chan') pubsub.subscribe('test-chan') assert pubsub.subscribed message = pubsub.get_message() assert message assert message['channel'] == 'test-chan', message assert message['type'] == 'subscribe', message message = pubsub.get_message() assert message assert message['channel'] == 'test-chan', message assert message['type'] == 'unsubscribe', message message = pubsub.get_message() assert message assert message['channel'] == 'test-chan', message assert message['type'] == 'subscribe', message assert pubsub.subscribed # <-- this fails! if __name__ == '__main__': test_sub_unsub_sub()

What Broke

Channels are forgotten, leading to missed messages and unregistered callbacks.

Why It Broke

A race condition occurs when unsubscribing and quickly resubscribing to a channel

Fix Options (Details)

Option A — Upgrade to fixed release Safe default (recommended)

pip install redis==4.0.0

When NOT to use: This fix is not suitable if the application requires strict message delivery guarantees.

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/1105

First fixed release: 4.0.0

Last verified: 2026-02-08. Validate in your environment.

Get updates

We publish verified fixes weekly. No spam.

Subscribe

When NOT to Use This Fix

  • This fix is not suitable if the application requires strict message delivery guarantees.

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

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

Version Compatibility Table

VersionStatus
4.0.0 Fixed

Related Issues

No related fixes found.

Sources

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