{"record":{"id":"6bc4e801d4ca6b5d","repo":"redis/redis-py","slug":"pattern-pattern-has-no-handler-registered","errorCode":null,"errorMessage":"Pattern: '{pattern}' has no handler registered","messagePattern":"Pattern: '(.+?)' has no handler registered","errorType":"exception","errorClass":"PubSubError","httpStatus":null,"severity":"error","filePath":"redis/asyncio/client.py","lineNumber":1746,"sourceCode":"        \"\"\"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:\n                if exception_handler is None:\n                    raise\n                res = exception_handler(e, self)","sourceCodeStart":1728,"sourceCodeEnd":1764,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/asyncio/client.py#L1728-L1764","documentation":"Companion to error 44 but for the patterns map: a psubscribed pattern has handler None when PubSubWorker.run() starts. run() requires every pattern subscription to carry a dispatch callback.","triggerScenarios":"Calling pubsub.psubscribe('news.*') positionally (handler stays None) and then awaiting pubsub.run(); or passing a None handler value in the psubscribe kwargs dict.","commonSituations":"Migrating from manual pattern polling to the run() worker model; copying a pattern-subscription snippet that used get_message into a run()-based task.","solutions":["Subscribe patterns with handlers: await pubsub.psubscribe(**{'news.*': handler}).","Use get_message polling if you want to handle messages inline instead of via callbacks."],"exampleFix":"// before\nawait pubsub.psubscribe('news.*')\nawait pubsub.run()\n// after\nasync def handler(msg): ...\nawait pubsub.psubscribe(**{'news.*': handler})\nawait pubsub.run()","handlingStrategy":"validation","validationCode":"missing = [p for p, h in pubsub.patterns.items() if h is None]\nassert not missing, f'patterns without handlers: {missing}'\nawait pubsub.run()","typeGuard":"def all_patterns_have_handlers(pubsub) -> bool:\n    return all(h is not None for h in pubsub.patterns.values())","tryCatchPattern":"try:\n    await pubsub.run()\nexcept PubSubError as e:\n    if 'no handler registered' in str(e):\n        # re-psubscribe with handlers, then run again\n        ...","preventionTips":["Always pass a callback dict to psubscribe when you intend to call run().","If polling manually, use get_message instead of run()."],"tags":["pubsub","callbacks","run-loop","pattern","handler"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}