redis/redis-py · error · PubSubError
Channel: '{channel}' has no handler registered
Error message
Channel: '{channel}' has no handler registered What it means
Raised in `PubSub.run_in_thread` when iterating `self.channels` and finding a subscription whose handler is None. `run_in_thread` requires every registered channel to have a callback (the thread dispatches messages via these handlers internally), so a None handler is treated as a configuration error and raises PubSubError before starting the worker thread.
Source
Thrown at redis/client.py:1727
elif message_type != "pong":
# this is a subscribe/unsubscribe message. ignore if we don't
# want them
if ignore_subscribe_messages or self.ignore_subscribe_messages:
return None
return message
def run_in_thread(
self,
sleep_time: float = 0.0,
daemon: bool = False,
exception_handler: Optional[Callable] = None,
pubsub=None,
sharded_pubsub: bool = False,
) -> "PubSubWorkerThread":
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")
for s_channel, handler in self.shard_channels.items():
if handler is None:
raise PubSubError(
f"Shard Channel: '{s_channel}' has no handler registered"
)
pubsub = self if pubsub is None else pubsub
thread = PubSubWorkerThread(
pubsub,
sleep_time,
daemon=daemon,
exception_handler=exception_handler,
sharded_pubsub=sharded_pubsub,
)
thread.start()View on GitHub (pinned to da03cdc7e8)
Solutions
- Pass a handler when subscribing for threaded use: `ps.subscribe(my_channel=my_handler)`.
- Ensure every already-subscribed channel has a handler — re-subscribe with handlers for any that are missing.
- If you want to poll manually instead of using callbacks, do not call run_in_thread; use a get_message loop in your own thread.
Example fix
# before
ps.subscribe('events')
ps.run_in_thread() # raises: Channel 'events' has no handler registered
# after
def on_event(msg):
print(msg)
ps.subscribe(events=on_event)
ps.run_in_thread() Defensive patterns
Strategy: validation
Validate before calling
# Before run_in_thread, verify every channel has a handler
if any(h is None for h in ps.channels.values()):
raise RuntimeError('Register handlers for all channels before run_in_thread')
ps.run_in_thread() Type guard
def channels_have_handlers(ps) -> bool:
return all(h is not None for h in ps.channels.values()) Prevention
- Use subscribe(channel=handler) form when planning to use run_in_thread.
- Never mix plain subscribe() with run_in_thread.
When it happens
Trigger: Subscribing with `ps.subscribe('ch')` (no callback) and then calling `ps.run_in_thread()`. The plain `subscribe()` call stores None as the handler; run_in_thread validates that handlers exist for every channel and rejects the configuration. To use run_in_thread you must subscribe with a callable: `ps.subscribe(ch=handler)`.
Common situations: Mixing the polling API (subscribe + get_message) with the threaded API (run_in_thread); porting code from a get_message loop to a worker thread without adding per-channel callbacks.
Related errors
- Pattern: '{pattern}' has no handler registered
- Shard Channel: '{s_channel}' has no handler registered
- Buffer is closed.
- Connection closed by server.
- pubsub connection not set: did you forget to call subscribe(
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/d89b2e1edf978300.json.
Report an issue: GitHub.