redis/redis-py · error · PubSubError
Channel: '{channel}' has no handler registered
Error message
Channel: '{channel}' has no handler registered What it means
Raised by the async PubSub worker's run() method (redis/asyncio/client.py:1743) when iterating self.channels and finding a channel whose registered handler is None. run() is the callback-driven message pump; it requires every subscribed channel to have a callback, otherwise it cannot dispatch. It raises PubSubError naming the offending channel.
Source
Thrown at redis/asyncio/client.py:1743
poll_timeout: float = 1.0,
pubsub=None,
) -> None:
"""Process pub/sub messages using registered callbacks.
This is the equivalent of :py:meth:`redis.PubSub.run_in_thread` in
redis-py, but it is a coroutine. To launch it as a separate task, use
``asyncio.create_task``:
>>> task = asyncio.create_task(pubsub.run())
To shut it down, use asyncio cancellation:
>>> task.cancel()
>>> await task
"""
for channel, handler in self.channels.items():
if handler is None:
raise PubSubError(f"Channel: '{channel}' has no handler registered")
for pattern, handler in self.patterns.items():
if handler is None:
raise PubSubError(f"Pattern: '{pattern}' has no handler registered")
await self.connect()
while True:
try:
if pubsub is None:
await self.get_message(
ignore_subscribe_messages=True, timeout=poll_timeout
)
else:
await pubsub.get_message(
ignore_subscribe_messages=True, timeout=poll_timeout
)
except asyncio.CancelledError:
raise
except BaseException as e:View on GitHub (pinned to da03cdc7e8)
Solutions
- Pass callbacks at subscribe time: `await pubsub.subscribe(**{'ch': my_handler})`.
- If you want manual control, use `await pubsub.get_message(timeout=...)` instead of run().
- Ensure every channel registered via subscribe has a non-None handler before calling run().
Example fix
// before
pubsub = r.pubsub()
await pubsub.subscribe('ch')
task = asyncio.create_task(pubsub.run())
// after
async def my_handler(msg):
...
pubsub = r.pubsub()
await pubsub.subscribe(**{'ch': my_handler})
task = asyncio.create_task(pubsub.run()) Defensive patterns
Strategy: validation
Validate before calling
async def handler(msg): ...
pubsub = r.pubsub()
await pubsub.subscribe(**{'ch': handler})
assert all(h is not None for h in pubsub.channels.values())
task = asyncio.create_task(pubsub.run()) Prevention
- Use the callback form subscribe(**{name: fn}) when you intend to call run().
- Before run(), assert every channel in pubsub.channels has a callable handler.
- Do not mix manual get_message() subscriptions with run().
When it happens
Trigger: Subscribing with the channel-only form `await pubsub.subscribe('ch')` (no callable) and then launching `await pubsub.run(...)` or `asyncio.create_task(pubsub.run())`. The callback form `pubsub.subscribe(**{'ch': handler})` is required for run().
Common situations: Mixing the manual get_message() style (no callbacks needed) with the run() worker style (callbacks mandatory); copy-pasting a subscribe() call into a run()-based example.
Related errors
- Pattern: '{pattern}' has no handler registered
- pubsub connection not set: did you forget to call subscribe(
- Cannot issue nested calls to MULTI
- Commands without an initial WATCH have already been issued
- Cannot issue a WATCH after a MULTI
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/0f6fd59c6655827c.json.
Report an issue: GitHub.