Skip to content

fix(pubsub): add timeout parameter to listen() to prevent Redis 8.0 timeout errors#4101

Closed
goingforstudying-ctrl wants to merge 3 commits into
redis:masterfrom
goingforstudying-ctrl:fix/pubsub-listen-timeout
Closed

fix(pubsub): add timeout parameter to listen() to prevent Redis 8.0 timeout errors#4101
goingforstudying-ctrl wants to merge 3 commits into
redis:masterfrom
goingforstudying-ctrl:fix/pubsub-listen-timeout

Conversation

@goingforstudying-ctrl

@goingforstudying-ctrl goingforstudying-ctrl commented Jun 5, 2026

Copy link
Copy Markdown

Fixes #4098

Adds an optional parameter to (sync and async) that is forwarded to , matching the existing API. This prevents on Redis 8.0.0+ when no messages arrive within the connection's (default 5s).


Note

Low Risk
Backward-compatible API default; behavior change is limited to pubsub listen loops and read timeout handling, with dedicated tests.

Overview
Adds an optional timeout argument to PubSub.listen() in both sync and async clients (fixes #4098). Previously listen() always blocked indefinitely via parse_response(block=True), which could surface connection read timeouts on Redis 8.0+ when idle. With a timeout, it now mirrors get_message(): non-blocking reads with a shrinking remaining budget using time.monotonic(), then exits the generator when the budget is exhausted; timeout=None keeps indefinite blocking.

New tests cover empty listen(timeout=…), delivering a message within the window, and timeout=None blocking until publish.

Reviewed by Cursor Bugbot for commit e9cb71b. Bugbot is set up for automated code reviews on this repo. Configure here.

@jit-ci

jit-ci Bot commented Jun 5, 2026

Copy link
Copy Markdown

Hi, I’m Jit, a friendly security platform designed to help developers build secure applications from day zero with an MVS (Minimal viable security) mindset.

In case there are security findings, they will be communicated to you as a comment inside the PR.

Hope you’ll enjoy using Jit.

Questions? Comments? Want to learn more? Get in touch with us.

Comment thread redis/client.py
Comment thread redis/client.py

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 95c21e6. Configure here.

Comment thread redis/client.py
@goingforstudying-ctrl

Copy link
Copy Markdown
Author

@cursor[bot] Thanks for the review! I've pushed a fix that addresses all three issues:

  1. Default timeout: Already fixed in the previous commit — timeout defaults to None (blocking), preserving backward-compatible behavior.
  2. Finite timeout never ends: The loop now correctly breaks when elapsed >= timeout, so list(p.listen(timeout=0.1)) will finish as expected.
  3. Full timeout each loop iteration: Fixed by tracking remaining = timeout - elapsed and passing remaining into parse_response() instead of the full timeout. This ensures the total wait time is bounded by the original timeout, not reset on every iteration.

Please take another look when you have a chance.

@petyaslavova

Copy link
Copy Markdown
Collaborator

Hi @goingforstudying-ctrl, thank you for your contribution! I'll take a look at it shortly.

@goingforstudying-ctrl

Copy link
Copy Markdown
Author

Pushed a follow-up fix for the cursor bot review comments on #4101:

Let me know if anything else is needed.

@goingforstudying-ctrl

Copy link
Copy Markdown
Author

Pushed a fix that tracks remaining timeout across loop iterations and passes it to parse_response instead of the full timeout each time. This should address the cumulative timeout drift issue. Let me know if anything else is needed.

goingforstudying-ctrl and others added 3 commits June 8, 2026 06:25
…imeout errors

- Sync PubSub.listen() now accepts an optional timeout parameter
- Async PubSub.listen() now accepts an optional timeout parameter
- Both delegate to parse_response(block=(timeout is None), timeout=timeout)
- Added 3 tests covering timeout=0.1, timeout=1.0 with message, timeout=None

Fixes redis#4098
- Change default timeout from 0.0 to None to preserve backward-compatible
  blocking behavior and prevent busy-looping when listen() is called
  without arguments.
- Add elapsed-time tracking so that listen(timeout=X) breaks out of the
  loop after the timeout expires, preventing infinite iteration when no
  message arrives.

Fixes cursor[bot] review comments:
1. 'Listen default timeout busy loops' — defaulting to 0.0 caused
   parse_response(block=False) to return None immediately, spinning the
   CPU.
2. 'Finite listen timeout never ends' — without elapsed-time tracking,
   the while self.subscribed loop would keep calling parse_response
   indefinitely even after the timeout period.
…timeout drift

- Track remaining timeout across loop iterations instead of passing
the full timeout each time. This ensures listen(timeout=N) returns
within approximately N seconds total, not N seconds per iteration.
- Addresses cursor[bot] review comments about full timeout being
passed each loop iteration.
@goingforstudying-ctrl goingforstudying-ctrl force-pushed the fix/pubsub-listen-timeout branch from 5586cea to e9cb71b Compare June 8, 2026 10:25
@petyaslavova

Copy link
Copy Markdown
Collaborator

Closing this PR as all the changes are added in #4103

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pubsub.listen() times out if no messages received in 5s

2 participants