redis/redis-py · error · PubSubError
Channel: ' ' has no handler registered
Error message
Channel: '{channel}' has no handler registered What it means
Raised by PubSub.run_in_thread() when a subscribed channel has no message handler callback registered. run_in_thread dispatches incoming messages to per-channel handlers; if any channel was subscribed without a handler, the worker thread cannot route messages, so the library refuses to start it.
Solutions
- Register a handler per channel: pubsub.subscribe(**{'channel': handler_fn}) or pubsub.subscribe(handler_fn, 'channel').
- If you intend to poll manually with get_message(), do not call run_in_thread() — use a plain loop instead.
- Ensure every channel in pubsub.channels has a non-None handler before calling run_in_thread().
Example fix
// before
pubsub.subscribe('alerts')
pubsub.run_in_thread() # PubSubError
// after
def on_alert(msg): ...
pubsub.subscribe(**{'alerts': on_alert})
pubsub.run_in_thread() Defensive patterns
Strategy: validation
Validate before calling
# Ensure every subscribed channel has a handler before starting the worker.
missing = [ch for ch, h in pubsub.channels.items() if h is None]
if missing:
raise ValueError(f'Channels missing handlers: {missing}')
pubsub.run_in_thread() Type guard
def all_channels_have_handlers(pubsub) -> bool:
return all(h is not None for h in pubsub.channels.values()) Try / catch
from redis.exceptions import PubSubError
try:
pubsub.run_in_thread()
except PubSubError:
# register handlers then retry
for ch in list(pubsub.channels):
pubsub.channels[ch] = default_handler Prevention
- When adopting run_in_thread, switch to subscribe(**{name: handler}) form.
- Do not mix polling-style subscribe(name) with run_in_thread().
- Validate pubsub.channels has no None values before starting the worker.
When it happens
Trigger: Calling pubsub.subscribe(channel) (positional, no callback) followed by pubsub.run_in_thread(). The channels dict maps the channel to None, and the validation loop at client.py:1725 raises.
Common situations: Mixing the two subscribe styles: subscribe(name) for manual get_message() polling vs. subscribe(name=handler) for run_in_thread dispatch; refactoring from polling to threaded mode without adding handlers.
Related errors
- Pattern: ' ' has no handler registered
- Shard Channel: ' ' has no handler registered
- pubsub connection not set: did you forget to call…
- A ``db`` querystring option can only be 0 in cluster mode
- A non health check response was cleaned by execute_command
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/d89b2e1edf978300.
Report an issue: GitHub.
Appendix: 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 6a6b581b48)