redis/redis-py · warning · RedisError
Channel '{channel}' has no handler registered
Error message
Channel '{channel}' has no handler registered What it means
Raised as redis.exceptions.RedisError by StandaloneKeyspaceNotifications._validate_all_handlers (redis/keyspace_notifications.py:1725). Before spawning the background worker thread via run_in_thread(), the manager checks every subscribed channel in the underlying PubSub has a handler; a None handler means a notification could arrive with no callback to dispatch it, so the start is aborted.
Source
Thrown at redis/keyspace_notifications.py:1725
Example:
>>> for notification in ksn.listen():
... print(f"{notification.key}: {notification.event_type}")
"""
while self.subscribed:
notification = self.get_message(timeout=1.0)
if notification is not None:
yield notification
@property
def subscribed(self) -> bool:
"""Check if there are any active subscriptions and not closed."""
return not self._closed and self._pubsub.subscribed
def _validate_all_handlers(self) -> None:
"""Raise if any subscription in the underlying PubSub lacks a handler."""
for channel, handler in self._pubsub.channels.items():
if handler is None:
raise RedisError(f"Channel '{channel}' has no handler registered")
for pattern, handler in self._pubsub.patterns.items():
if handler is None:
raise RedisError(f"Pattern '{pattern}' has no handler registered")
def close(self):
"""Close the pubsub connection and clean up resources."""
self._closed = True
try:
self._pubsub.close()
except Exception:
pass
# =============================================================================
# Cluster-Aware Keyspace Notification Manager
# =============================================================================
View on GitHub (pinned to da03cdc7e8)
Solutions
- Pass handler= to subscribe()/subscribe_keyspace() for every channel before calling run_in_thread().
- If you intend to poll manually, use get_message()/listen() instead of run_in_thread().
- Re-subscribe with handlers, or unsubscribe the handler-less channels before starting the worker.
Example fix
// before
notifications.subscribe(KeyspaceChannel('user:*')) # no handler
thread = notifications.run_in_thread(poll_timeout=0.1) # raises
// after
def on_evt(n):
print(n.key, n.event_type)
notifications.subscribe(KeyspaceChannel('user:*'), handler=on_evt)
thread = notifications.run_in_thread(poll_timeout=0.1) Defensive patterns
Strategy: validation
Validate before calling
# ensure every channel has a handler before run_in_thread
for ch, h in notifications._pubsub.channels.items():
if h is None:
raise RuntimeError(f'channel {ch!r} lacks a handler')
notifications.run_in_thread(poll_timeout=0.1) Type guard
def all_channels_have_handler(ksn) -> bool:
return all(h is not None for h in ksn._pubsub.channels.values()) Prevention
- Always pass handler= to subscribe()/subscribe_keyspace() when you plan to use run_in_thread().
- Use get_message()/listen() for handler-less polling instead of the worker thread.
- Unsubscribe handler-less channels before starting the worker.
- Pick one dispatch model (handlers OR manual polling) per subscription and stick to it.
When it happens
Trigger: Calling notifications.run_in_thread() (standalone Redis) after subscribe(channel) was called WITHOUT a handler= callback, leaving the PubSub channel registered with a None handler.
Common situations: Mixing the handler-based API (run_in_thread) with the polling API (get_message/listen) on the same subscriptions; subscribing for polling then later trying to run_in_thread without re-subscribing with handlers.
Related errors
- Pattern '{pattern}' has no handler registered
- Buffer is closed.
- Subcommand {subcommand_name} not found in command {command_n
- Command {command_name} not found in commands
- Connection closed by server.
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/6176f3d429703bcf.json.
Report an issue: GitHub.