{"record":{"id":"0f6fd59c6655827c","repo":"redis/redis-py","slug":"channel-channel-has-no-handler-registered","errorCode":null,"errorMessage":"Channel: '{channel}' has no handler registered","messagePattern":"Channel: '(.+?)' has no handler registered","errorType":"exception","errorClass":"PubSubError","httpStatus":null,"severity":"error","filePath":"redis/asyncio/client.py","lineNumber":1743,"sourceCode":"        poll_timeout: float = 1.0,\n        pubsub=None,\n    ) -> None:\n        \"\"\"Process pub/sub messages using registered callbacks.\n\n        This is the equivalent of :py:meth:`redis.PubSub.run_in_thread` in\n        redis-py, but it is a coroutine. To launch it as a separate task, use\n        ``asyncio.create_task``:\n\n            >>> task = asyncio.create_task(pubsub.run())\n\n        To shut it down, use asyncio cancellation:\n\n            >>> task.cancel()\n            >>> await task\n        \"\"\"\n        for channel, handler in self.channels.items():\n            if handler is None:\n                raise PubSubError(f\"Channel: '{channel}' has no handler registered\")\n        for pattern, handler in self.patterns.items():\n            if handler is None:\n                raise PubSubError(f\"Pattern: '{pattern}' has no handler registered\")\n\n        await self.connect()\n        while True:\n            try:\n                if pubsub is None:\n                    await self.get_message(\n                        ignore_subscribe_messages=True, timeout=poll_timeout\n                    )\n                else:\n                    await pubsub.get_message(\n                        ignore_subscribe_messages=True, timeout=poll_timeout\n                    )\n            except asyncio.CancelledError:\n                raise\n            except BaseException as e:","sourceCodeStart":1725,"sourceCodeEnd":1761,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/asyncio/client.py#L1725-L1761","documentation":"Raised at the start of PubSubWorker.run() when iterating self.channels: a channel is present in the subscription map but its handler callback is None. The run loop dispatches messages through registered handlers, so a subscribed channel with no handler is an unrecoverable programming error.","triggerScenarios":"Calling pubsub.subscribe(**{channel: callback}) where the callback value is None, then awaiting pubsub.run(...); or mixing positional subscribe('ch') (which leaves handler None) with the callback-based run() API.","commonSituations":"Switching from the manual get_message polling style to the run()-with-handlers style without re-subscribing with handlers; passing a typo'd variable that resolves to None as the handler.","solutions":["Subscribe with a real callback: await pubsub.subscribe(**{'my-channel': my_handler}).","If you intend to poll messages yourself (no handlers), use get_message in a loop instead of run().","Before run(), assert none of pubsub.channels.values() is None."],"exampleFix":"// before\nawait pubsub.subscribe('my-channel')\nawait pubsub.run()\n// after\nasync def handler(msg): ...\nawait pubsub.subscribe(**{'my-channel': handler})\nawait pubsub.run()","handlingStrategy":"validation","validationCode":"missing = [ch for ch, h in pubsub.channels.items() if h is None]\nassert not missing, f'channels without handlers: {missing}'\nawait pubsub.run()","typeGuard":"def all_channels_have_handlers(pubsub) -> bool:\n    return all(h is not None for h in pubsub.channels.values())","tryCatchPattern":"try:\n    await pubsub.run()\nexcept PubSubError as e:\n    if 'no handler registered' in str(e):\n        # re-subscribe with handlers, then run again\n        ...","preventionTips":["When using run(), always subscribe via the kwargs form: subscribe(**{ch: cb}).","Keep handlers in a dict so you can assert coverage before run()."],"tags":["pubsub","callbacks","run-loop","handler"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}