{"id":"06f569361e5739d4","repo":"redis/redis-py","slug":"pubsub-is-not-supported-for-rediscluster","errorCode":null,"errorMessage":"PubSub is not supported for RedisCluster","messagePattern":"PubSub is not supported for RedisCluster","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"redis/asyncio/multidb/command_executor.py","lineNumber":224,"sourceCode":"    def active_pubsub(self) -> Optional[PubSub]:\n        return self._active_pubsub\n\n    @active_pubsub.setter\n    def active_pubsub(self, pubsub: PubSub) -> None:\n        self._active_pubsub = pubsub\n\n    @property\n    def failover_strategy_executor(self) -> FailoverStrategyExecutor:\n        return self._failover_strategy_executor\n\n    @property\n    def command_retry(self) -> Retry:\n        return self._command_retry\n\n    def pubsub(self, **kwargs):\n        if self._active_pubsub is None:\n            if isinstance(self._active_database.client, RedisCluster):\n                raise ValueError(\"PubSub is not supported for RedisCluster\")\n\n            self._active_pubsub = self._active_database.client.pubsub(**kwargs)\n            self._active_pubsub_kwargs = kwargs\n\n    async def execute_command(self, *args, **options):\n        async def callback():\n            response = await self._active_database.client.execute_command(\n                *args, **options\n            )\n            await self._register_command_execution(args)\n            return response\n\n        return await self._execute_with_failure_detection(callback, args)\n\n    async def execute_pipeline(self, command_stack: tuple):\n        async def callback():\n            async with self._active_database.client.pipeline() as pipe:\n                for command, options in command_stack:","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/asyncio/multidb/command_executor.py#L206-L242","documentation":"Raised by `DefaultCommandExecutor.pubsub()` (redis/asyncio/multidb/command_executor.py:224) when the active database's underlying client is a `RedisCluster`. The multi-database PubSub abstraction assumes a standalone-style PubSub object; cluster PubSub has a different shape, so creating one through the executor is rejected with ValueError.","triggerScenarios":"Calling `await multi_client.pubsub()` (or constructing the PubSub wrapper which calls `command_executor.pubsub()` at client.py:558) when `command_executor.active_database.client` is an `AsyncRedisCluster` instance — i.e. `MultiDbConfig.client_class = RedisCluster` and the active DB uses a cluster client.","commonSituations":"Configuring `client_class=RedisCluster` in MultiDbConfig and then trying to subscribe to pub/sub channels; failing over to a cluster-backed active database and then calling pubsub.","solutions":["Use a standalone `redis.asyncio.Redis` client (not via MultiDBClient) for pub/sub against a cluster endpoint, or use `RedisCluster.pubsub()` directly on a dedicated cluster client.","Configure `MultiDbConfig.client_class=Redis` if pub/sub through the multi-database client is required and topology allows standalone connections.","Subscribe before failover to a cluster DB, or restrict pub/sub usage to standalone-backed databases."],"exampleFix":"# before\ncfg = MultiDbConfig(databases_config=[...], client_class=RedisCluster)\nclient = MultiDBClient(cfg)\nawait client.initialize()\nps = await client.pubsub()  # ValueError: PubSub is not supported for RedisCluster\n\n# after\nimport redis.asyncio as redis\ncluster = redis.RedisCluster.from_url('redis://cluster:16379')\nps = cluster.pubsub()  # use the cluster client's own pubsub","handlingStrategy":"validation","validationCode":"def can_pubsub(client) -> bool:\n    # PubSub through MultiDBClient requires a non-cluster active DB\n    from redis.asyncio import RedisCluster\n    from redis.asyncio.multidb.client import MultiDBClient\n    if not isinstance(client, MultiDBClient):\n        return True\n    active = client.command_executor.active_database\n    return active is not None and not isinstance(active.client, RedisCluster)","typeGuard":"import redis.asyncio as aioredis\nfrom redis.asyncio.multidb.client import MultiDBClient\n\ndef active_db_is_standalone(client) -> bool:\n    if not isinstance(client, MultiDBClient):\n        return True\n    active = client.command_executor.active_database\n    return active is not None and isinstance(active.client, aioredis.Redis)","tryCatchPattern":"try:\n    ps = await client.pubsub()\nexcept ValueError as e:\n    if 'PubSub is not supported for RedisCluster' in str(e):\n        # use a dedicated cluster client's pubsub instead\n        cluster = redis.asyncio.RedisCluster.from_url(url)\n        ps = cluster.pubsub()\n    else:\n        raise","preventionTips":["Use a dedicated `redis.asyncio.Redis` or `RedisCluster` client for pub/sub, not MultiDBClient, when the topology is cluster.","Set `MultiDbConfig.client_class=Redis` if pub/sub must flow through MultiDBClient.","Gate `await client.pubsub()` with an isinstance check on the active DB client."],"tags":["multidb","pubsub","cluster","unsupported"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}