redis/redis-py · error · PubSubError

Channel: '{channel}' has no handler registered

Error message

Channel: '{channel}' has no handler registered

What it means

Raised in `PubSub.run_in_thread` when iterating `self.channels` and finding a subscription whose handler is None. `run_in_thread` requires every registered channel to have a callback (the thread dispatches messages via these handlers internally), so a None handler is treated as a configuration error and raises PubSubError before starting the worker thread.

Source

Thrown at redis/client.py:1727

        elif message_type != "pong":
            # this is a subscribe/unsubscribe message. ignore if we don't
            # want them
            if ignore_subscribe_messages or self.ignore_subscribe_messages:
                return None

        return message

    def run_in_thread(
        self,
        sleep_time: float = 0.0,
        daemon: bool = False,
        exception_handler: Optional[Callable] = None,
        pubsub=None,
        sharded_pubsub: bool = False,
    ) -> "PubSubWorkerThread":
        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")
        for s_channel, handler in self.shard_channels.items():
            if handler is None:
                raise PubSubError(
                    f"Shard Channel: '{s_channel}' has no handler registered"
                )

        pubsub = self if pubsub is None else pubsub
        thread = PubSubWorkerThread(
            pubsub,
            sleep_time,
            daemon=daemon,
            exception_handler=exception_handler,
            sharded_pubsub=sharded_pubsub,
        )
        thread.start()

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Pass a handler when subscribing for threaded use: `ps.subscribe(my_channel=my_handler)`.
  2. Ensure every already-subscribed channel has a handler — re-subscribe with handlers for any that are missing.
  3. If you want to poll manually instead of using callbacks, do not call run_in_thread; use a get_message loop in your own thread.

Example fix

# before
ps.subscribe('events')
ps.run_in_thread()  # raises: Channel 'events' has no handler registered

# after
def on_event(msg):
    print(msg)
ps.subscribe(events=on_event)
ps.run_in_thread()
Defensive patterns

Strategy: validation

Validate before calling

# Before run_in_thread, verify every channel has a handler
if any(h is None for h in ps.channels.values()):
    raise RuntimeError('Register handlers for all channels before run_in_thread')
ps.run_in_thread()

Type guard

def channels_have_handlers(ps) -> bool:
    return all(h is not None for h in ps.channels.values())

Prevention

When it happens

Trigger: Subscribing with `ps.subscribe('ch')` (no callback) and then calling `ps.run_in_thread()`. The plain `subscribe()` call stores None as the handler; run_in_thread validates that handlers exist for every channel and rejects the configuration. To use run_in_thread you must subscribe with a callable: `ps.subscribe(ch=handler)`.

Common situations: Mixing the polling API (subscribe + get_message) with the threaded API (run_in_thread); porting code from a get_message loop to a worker thread without adding per-channel callbacks.

Related errors


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