redis/redis-py · error · PubSubError

Pattern: ' ' has no handler registered

Error message

Pattern: '{pattern}' has no handler registered

What it means

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.

Solutions

  1. Subscribe patterns with handlers: await pubsub.psubscribe(**{'news.*': handler}).
  2. Use get_message polling if you want to handle messages inline instead of via callbacks.

Example fix

// before
await pubsub.psubscribe('news.*')
await pubsub.run()
// after
async def handler(msg): ...
await pubsub.psubscribe(**{'news.*': handler})
await pubsub.run()
Defensive patterns

Strategy: validation

Validate before calling

missing = [p for p, h in pubsub.patterns.items() if h is None]
assert not missing, f'patterns without handlers: {missing}'
await pubsub.run()

Type guard

def all_patterns_have_handlers(pubsub) -> bool:
    return all(h is not None for h in pubsub.patterns.values())

Try / catch

try:
    await pubsub.run()
except PubSubError as e:
    if 'no handler registered' in str(e):
        # re-psubscribe with handlers, then run again
        ...

Prevention

When it happens

Trigger: Calling pubsub.psubscribe('news.*') positionally (handler stays None) and then awaiting pubsub.run(); or passing a None handler value in the psubscribe kwargs dict.

Common situations: Migrating from manual pattern polling to the run() worker model; copying a pattern-subscription snippet that used get_message into a run()-based task.

Related errors


AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10). Data as JSON: /api/errors/6bc4e801d4ca6b5d. Report an issue: GitHub.

Appendix: 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 6a6b581b48)