{"id":"6176f3d429703bcf","repo":"redis/redis-py","slug":"channel-channel-has-no-handler-registered-6176f3","errorCode":null,"errorMessage":"Channel '{channel}' has no handler registered","messagePattern":"Channel '(.+?)' has no handler registered","errorType":"exception","errorClass":"RedisError","httpStatus":null,"severity":"warning","filePath":"redis/keyspace_notifications.py","lineNumber":1725,"sourceCode":"        Example:\n            >>> for notification in ksn.listen():\n            ...     print(f\"{notification.key}: {notification.event_type}\")\n        \"\"\"\n        while self.subscribed:\n            notification = self.get_message(timeout=1.0)\n            if notification is not None:\n                yield notification\n\n    @property\n    def subscribed(self) -> bool:\n        \"\"\"Check if there are any active subscriptions and not closed.\"\"\"\n        return not self._closed and self._pubsub.subscribed\n\n    def _validate_all_handlers(self) -> None:\n        \"\"\"Raise if any subscription in the underlying PubSub lacks a handler.\"\"\"\n        for channel, handler in self._pubsub.channels.items():\n            if handler is None:\n                raise RedisError(f\"Channel '{channel}' has no handler registered\")\n        for pattern, handler in self._pubsub.patterns.items():\n            if handler is None:\n                raise RedisError(f\"Pattern '{pattern}' has no handler registered\")\n\n    def close(self):\n        \"\"\"Close the pubsub connection and clean up resources.\"\"\"\n        self._closed = True\n        try:\n            self._pubsub.close()\n        except Exception:\n            pass\n\n\n# =============================================================================\n# Cluster-Aware Keyspace Notification Manager\n# =============================================================================\n\n","sourceCodeStart":1707,"sourceCodeEnd":1743,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/keyspace_notifications.py#L1707-L1743","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nnotifications.subscribe(KeyspaceChannel('user:*'))  # no handler\nthread = notifications.run_in_thread(poll_timeout=0.1)  # raises\n\n// after\ndef on_evt(n):\n    print(n.key, n.event_type)\nnotifications.subscribe(KeyspaceChannel('user:*'), handler=on_evt)\nthread = notifications.run_in_thread(poll_timeout=0.1)","handlingStrategy":"validation","validationCode":"# ensure every channel has a handler before run_in_thread\nfor ch, h in notifications._pubsub.channels.items():\n    if h is None:\n        raise RuntimeError(f'channel {ch!r} lacks a handler')\nnotifications.run_in_thread(poll_timeout=0.1)","typeGuard":"def all_channels_have_handler(ksn) -> bool:\n    return all(h is not None for h in ksn._pubsub.channels.values())","tryCatchPattern":null,"preventionTips":["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."],"tags":["keyspace-notifications","pubsub","validation","rediserror"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}