{"record":{"id":"c98fc5775361023e","repo":"redis/redis-py","slug":"bad-response-from-ping-health-check","errorCode":null,"errorMessage":"Bad response from PING health check","messagePattern":"Bad response from PING health check","errorType":"exception","errorClass":"ConnectionError","httpStatus":null,"severity":"error","filePath":"redis/asyncio/connection.py","lineNumber":1149,"sourceCode":"            await record_connection_closed(\n                close_reason=CloseReason.APPLICATION_CLOSE,\n            )\n\n        if self.maintenance_state == MaintenanceState.MAINTENANCE:\n            # MOVING state is owned by the pool-level TTL cleanup. Regular\n            # maintenance timeout relaxation can be restored when this\n            # connection closes, matching the sync lifecycle.\n            self.reset_tmp_settings(reset_relaxed_timeout=True)\n            self.maintenance_state = MaintenanceState.NONE\n            # reset the sets that keep track of received start maint\n            # notifications and skipped end maint notifications\n            self.reset_received_notifications()\n\n    async def _send_ping(self):\n        \"\"\"Send PING, expect PONG in return\"\"\"\n        await self.send_command(\"PING\", check_health=False)\n        if str_if_bytes(await self.read_response()) != \"PONG\":\n            raise ConnectionError(\"Bad response from PING health check\")\n\n    async def _ping_failed(self, error, failure_count):\n        \"\"\"Function to call when PING fails\"\"\"\n        await self.disconnect(\n            error=error, failure_count=failure_count, health_check_failed=True\n        )\n\n    async def check_health(self):\n        \"\"\"Check the health of the connection with a PING/PONG\"\"\"\n        if (\n            self.health_check_interval\n            and asyncio.get_running_loop().time() > self.next_health_check\n        ):\n            await self.retry.call_with_retry(\n                self._send_ping, self._ping_failed, with_failure_count=True\n            )\n\n    async def _send_packed_command(self, command: Iterable[bytes]) -> None:","sourceCodeStart":1131,"sourceCodeEnd":1167,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/asyncio/connection.py#L1131-L1167","documentation":"Raised as ConnectionError from _send_ping() when a PING returns a response that is not the literal 'PONG'. This is the health-check probe invoked by check_health() before commands when health_check_interval is set. A non-PONG reply indicates the server (or a middlebox) returned unexpected bytes on the same socket.","triggerScenarios":"Setting health_check_interval=N on the client and the elapsed interval triggers a PING; a proxy/SSL-terminator/reset returns something other than PONG (e.g. an error frame, an AUTH-required reply, or a connection that was silently reassigned).","commonSituations":"A load balancer or RESP-unaware proxy injecting its own response; the server sent a maintenance/notification frame the parser surfaced as the PING reply; shared connection handed off mid-stream in a forked/branching process; RESP2/RESP3 mismatch causing a push reply to be read as the PONG.","solutions":["Inspect what the server actually returns by running redis-cli PING against the same endpoint.","Remove any non-RESP-aware proxy between the client and Redis, or configure it for transparent L4 passthrough.","Temporarily raise or disable health_check_interval to isolate whether the failure is the probe vs. real traffic.","Confirm protocol= matches the server's capability (RESP3 push types can confuse an older server)."],"exampleFix":"// before\nr = redis.asyncio.Redis(host=h, health_check_interval=1)\n// after\nr = redis.asyncio.Redis(host=h, health_check_interval=30)","handlingStrategy":"retry","validationCode":"null","typeGuard":"from redis.exceptions import ConnectionError\n\ndef is_bad_ping(exc: BaseException) -> bool:\n    return isinstance(exc, ConnectionError) and 'PING' in str(exc)","tryCatchPattern":"from redis.exceptions import ConnectionError\nfrom redis.retry import Retry\nfrom redis.backoff import ExponentialBackoff\n\n# retry the health-check-driven failure automatically\nr = redis.asyncio.Redis(\n    host=h,\n    health_check_interval=30,\n    retry=Retry(ExponentialBackoff(), 2),\n    retry_on_error=[ConnectionError],\n)","preventionTips":["Use a RESP-aware (L4) proxy; never inject bytes into the Redis stream.","Match protocol= to server capability.","Raise health_check_interval to avoid aggressive probing on slow links."],"tags":["health-check","ping","connection","async","protocol","proxy"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}