{"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/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/asyncio/client.py#L1728-L1764","documentation":"Raised by PubSub.run() (redis/asyncio/client.py:1746) when iterating self.patterns and finding a pattern whose handler is None. Symmetric to the channel case: the callback worker requires every psubscribe() pattern to carry a callable. It raises PubSubError naming the offending pattern.","triggerScenarios":"Calling `await pubsub.psubscribe('chan:*')` without a callback then starting `pubsub.run()`. Pattern handlers must be passed as `pubsub.psubscribe(**{'chan:*': handler})`.","commonSituations":"Subscribing to glob patterns for manual dispatch but then switching to the run() worker without converting subscriptions to the callback form.","solutions":["Register a callback per pattern: `await pubsub.psubscribe(**{'chan:*': handler})`.","Use get_message() instead of run() for manual dispatch.","Before run(), assert every entry in pubsub.patterns has a non-None handler."],"exampleFix":"// before\nawait pubsub.psubscribe('chan:*')\ntask = asyncio.create_task(pubsub.run())\n// after\nasync def handler(msg):\n    ...\nawait pubsub.psubscribe(**{'chan:*': handler})\ntask = asyncio.create_task(pubsub.run())","handlingStrategy":"validation","validationCode":"async def handler(msg): ...\npubsub = r.pubsub()\nawait pubsub.psubscribe(**{'chan:*': handler})\nassert all(h is not None for h in pubsub.patterns.values())\ntask = asyncio.create_task(pubsub.run())","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pass callbacks for every psubscribe() pattern when using run().","Assert no None handlers exist in pubsub.patterns before launching the worker.","Use get_message() for manual pattern dispatch."],"tags":["redis","asyncio","pubsub","callback-worker","api-misuse"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}