redis/redis-py · error · PubSubError

Pattern: '{pattern}' has no handler registered

Error message

Pattern: '{pattern}' has no handler registered

What it means

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.

Source

Thrown at redis/asyncio/client.py:1746

        """Process pub/sub messages using registered callbacks.

        This is the equivalent of :py:meth:`redis.PubSub.run_in_thread` in
        redis-py, but it is a coroutine. To launch it as a separate task, use
        ``asyncio.create_task``:

            >>> task = asyncio.create_task(pubsub.run())

        To shut it down, use asyncio cancellation:

            >>> task.cancel()
            >>> await task
        """
        for channel, handler in self.channels.items():
            if handler is None:
                raise PubSubError(f"Channel: '{channel}' has no handler registered")
        for pattern, handler in self.patterns.items():
            if handler is None:
                raise PubSubError(f"Pattern: '{pattern}' has no handler registered")

        await self.connect()
        while True:
            try:
                if pubsub is None:
                    await self.get_message(
                        ignore_subscribe_messages=True, timeout=poll_timeout
                    )
                else:
                    await pubsub.get_message(
                        ignore_subscribe_messages=True, timeout=poll_timeout
                    )
            except asyncio.CancelledError:
                raise
            except BaseException as e:
                if exception_handler is None:
                    raise
                res = exception_handler(e, self)

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Register a callback per pattern: `await pubsub.psubscribe(**{'chan:*': handler})`.
  2. Use get_message() instead of run() for manual dispatch.
  3. Before run(), assert every entry in pubsub.patterns has a non-None handler.

Example fix

// before
await pubsub.psubscribe('chan:*')
task = asyncio.create_task(pubsub.run())
// after
async def handler(msg):
    ...
await pubsub.psubscribe(**{'chan:*': handler})
task = asyncio.create_task(pubsub.run())
Defensive patterns

Strategy: validation

Validate before calling

async def handler(msg): ...
pubsub = r.pubsub()
await pubsub.psubscribe(**{'chan:*': handler})
assert all(h is not None for h in pubsub.patterns.values())
task = asyncio.create_task(pubsub.run())

Prevention

When it happens

Trigger: Calling `await pubsub.psubscribe('chan:*')` without a callback then starting `pubsub.run()`. Pattern handlers must be passed as `pubsub.psubscribe(**{'chan:*': handler})`.

Common situations: Subscribing to glob patterns for manual dispatch but then switching to the run() worker without converting subscriptions to the callback form.

Related errors


AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04). Data as JSON: /data/errors/6bc4e801d4ca6b5d.json. Report an issue: GitHub.