{"id":"03998d9e4dd1fccc","repo":"redis/redis-py","slug":"a-non-health-check-response-was-cleaned-by-execute","errorCode":null,"errorMessage":"A non health check response was cleaned by execute_command: {response}","messagePattern":"A non health check response was cleaned by execute_command: (.+?)","errorType":"exception","errorClass":"PubSubError","httpStatus":null,"severity":"error","filePath":"redis/client.py","lineNumber":1247,"sourceCode":"        kwargs = {\"check_health\": not self.subscribed}\n        if not self.subscribed:\n            self.clean_health_check_responses()\n        with self._lock:\n            self._execute(connection, connection.send_command, *args, **kwargs)\n\n    def clean_health_check_responses(self) -> None:\n        \"\"\"\n        If any health check responses are present, clean them\n        \"\"\"\n        ttl = 10\n        conn = self.connection\n        while conn and self.health_check_response_counter > 0 and ttl > 0:\n            if self._execute(conn, conn.can_read, timeout=conn.socket_timeout):\n                response = self._execute(conn, conn.read_response)\n                if self.is_health_check_response(response):\n                    self.health_check_response_counter -= 1\n                else:\n                    raise PubSubError(\n                        \"A non health check response was cleaned by \"\n                        \"execute_command: {}\".format(response)\n                    )\n            ttl -= 1\n\n    def _reconnect(\n        self,\n        conn,\n        error: Optional[Exception] = None,\n        failure_count: Optional[int] = None,\n        start_time: Optional[float] = None,\n        command_name: Optional[str] = None,\n    ) -> None:\n        \"\"\"\n        The supported exceptions are already checked in the\n        retry object so we don't need to do it here.\n\n        In this error handler we are trying to reconnect to the server.","sourceCodeStart":1229,"sourceCodeEnd":1265,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/client.py#L1229-L1265","documentation":"Raised in `PubSub.clean_health_check_responses` while draining queued health-check PING replies. The client periodically sends `PING <health-check-message>` to keep the pubsub socket alive; when it later drains those replies it expects every consumed frame to be the matching health-check response. If a frame is consumed that is NOT a health-check reply, a `PubSubError` is raised because the socket's framing has desynchronized and continuing would corrupt the message stream.","triggerScenarios":"A PubSub instance with `health_check_interval` configured loses ordering between the health-check PING and other pubsub traffic — typically because the connection was shared or reused, a server-pushed message arrived interleaved with the PING reply, the connection dropped and reconnected mid-drain, or a RESP3 push notification was mistaken for a reply. The loop (ttl=10) drains up to 10 frames and raises on the first non-health-check frame.","commonSituations":"Long-lived pubsub listeners on unstable networks where reconnects race with health checks; RESP3 mode with push handlers enabled; mixed usage of the same connection for pubsub and normal commands; version changes that altered push/health-check framing.","solutions":["Ensure the PubSub connection is used exclusively for pubsub (never share it with normal command traffic) — call `pubsub = client.pubsub()` and only use pubsub methods on it.","If using RESP3, configure a proper push handler (`push_handler_func`) so push notifications are routed correctly and do not sit on the response stream.","Recreate the PubSub object and re-subscribe after the error — the connection state is corrupted and cannot be recovered in place.","Review `health_check_interval` and `socket_timeout`; if health checks fire faster than the consumer drains them, raise the interval or ensure `get_message()` is polled frequently."],"exampleFix":"# before\nps = client.pubsub()\nps.subscribe('ch')\n# same client used elsewhere -> interleaved traffic\n\n# after\nps = client.pubsub()\nps.subscribe('ch')\nfor msg in ps.listen():\n    handle(msg)  # drain promptly, keep this connection pubsub-only","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"from redis.exceptions import PubSubError\ntry:\n    msg = ps.get_message(timeout=1.0)\nexcept PubSubError:\n    # connection framing is corrupted — recreate pubsub and re-subscribe\n    ps.close()\n    ps = client.pubsub()\n    ps.subscribe('ch')","preventionTips":["Dedicate the pubsub connection to pubsub only — never run normal commands on it.","In RESP3, configure a push_handler_func so push frames do not sit on the response stream.","Poll get_message frequently so health-check replies are drained promptly."],"tags":["pubsub","health-check","connection","resp3"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}