Jump to solution
Verify

The Fix

Fixes a race condition in handling health checks for pub-sub by allowing the pubsub's method get_message() to be called without prior subscription.

Based on closed redis/redis-py issue #1720 · 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
@@ -1276,18 +1276,17 @@ def __init__( self.ignore_subscribe_messages = ignore_subscribe_messages self.connection = None + self.subscribed_event = threading.Event() # we need to know the encoding options for this connection in order # to lookup channel and pattern names for callback handlers.
repro.py
#!/usr/bin/env python3 import threading import time import redis def poll(ps): while True: message = ps.get_message(timeout=5) if message is not None: print(message) else: break def main(): r = redis.Redis.from_url("redis://localhost", health_check_interval=1) ps = r.pubsub() ps.subscribe("foo") poller = threading.Thread(target=poll, args=(ps,)) poller.start() ps.unsubscribe("foo") time.sleep(3) ps.subscribe("baz") poller.join() main()
verify
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
fix.md
Option A — Apply the official fix\nFixes a race condition in handling health checks for pub-sub by allowing the pubsub's method get_message() to be called without prior subscription.\nWhen NOT to use: This fix is not safe if the application relies on immediate unsubscribe confirmations.\n\n

Why This Fix Works in Production

  • Trigger: {'type': 'subscribe', 'pattern': None, 'channel': b'foo', 'data': 1}
  • Mechanism: Race condition occurs when health checks are issued before unsubscribe confirmation is received
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

  • Shows up under Python 3.5.1 in real deployments (not just unit tests).
  • Race condition occurs when health checks are issued before unsubscribe confirmation is received
  • Surfaces as: {'type': 'subscribe', 'pattern': None, 'channel': b'foo', 'data': 1}

Proof / Evidence

Discussion

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

“> In this case, the health check will be performed and we will still get a race reading from the socket with the poller thread”
@bmerry · 2021-11-21 · source
“Hey, Thanks for finding it and bringing it up! I was able to reproduce the bug and to find the RC”
@barshaul · 2021-11-21 · source

Failure Signature (Search String)

  • {'type': 'subscribe', 'pattern': None, 'channel': b'foo', 'data': 1}

Error Message

Stack trace
error.txt
Error Message ------------- {'type': 'subscribe', 'pattern': None, 'channel': b'foo', 'data': 1} {'type': 'unsubscribe', 'pattern': None, 'channel': b'foo', 'data': 0} {'type': 'subscribe', 'pattern': None, 'channel': b'baz', 'data': 1} {'type': 'subscribe', 'pattern': None, 'channel': b'foo', 'data': 1} {'type': 'unsubscribe', 'pattern': None, 'channel': b'foo', 'data': 0} {'type': 'subscribe', 'pattern': None, 'channel': b'baz', 'data': 1} {'type': 'subscribe', 'pattern': None, 'channel': b'foo', 'data': 1} {'type': 'unsubscribe', 'pattern': None, 'channel': b'foo', 'data': 0} {'type': 'subscribe', 'pattern': None, 'channel': b'baz', 'data': 1} {'type': 'subscribe', 'pattern': None, 'channel': b'foo', 'data': 1} {'type': 'unsubscribe', 'pattern': None, 'channel': b'foo', 'data': 0} {'type': 'subscribe', 'pattern': None, 'channel': b'baz', 'data': 1} {'type': 'subscribe', 'pattern': None, 'channel': b'foo', 'data': 1} {'type': 'unsubscribe', 'pattern': None, 'channel': b'foo', 'data': 0} {'type': 'subscribe', 'pattern': None, 'channel': b'baz', 'data': 1} {'type': 'subscribe', 'pattern': None, 'channel': b'foo', 'data': 1} {'type': 'unsubscribe', 'pattern': None, 'channel': b'foo', 'data': 0} {'type': 80, 'pattern': None, 'channel': 79, 'data': 78} Traceback (most recent call last): File "/home/bmerry/work/sdp/env3/lib/python3.8/site-packages/redis/connection.py", line 430, in read_from_socket Exceptio ... (truncated) ...

Minimal Reproduction

repro.py
#!/usr/bin/env python3 import threading import time import redis def poll(ps): while True: message = ps.get_message(timeout=5) if message is not None: print(message) else: break def main(): r = redis.Redis.from_url("redis://localhost", health_check_interval=1) ps = r.pubsub() ps.subscribe("foo") poller = threading.Thread(target=poll, args=(ps,)) poller.start() ps.unsubscribe("foo") time.sleep(3) ps.subscribe("baz") poller.join() main()

Environment

  • Python: 3.5.1

What Broke

Health checks may return incorrect responses, causing unexpected behavior in pub-sub messaging.

Why It Broke

Race condition occurs when health checks are issued before unsubscribe confirmation is received

Fix Options (Details)

Option A — Apply the official fix

Fixes a race condition in handling health checks for pub-sub by allowing the pubsub's method get_message() to be called without prior subscription.

When NOT to use: This fix is not safe if the application relies on immediate unsubscribe confirmations.

Fix reference: https://github.com/redis/redis-py/pull/1737

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

Get updates

We publish verified fixes weekly. No spam.

Subscribe

When NOT to Use This Fix

  • This fix is not safe if the application relies on immediate unsubscribe confirmations.

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

Related Issues

No related fixes found.

Sources

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