redis/redis-py · error · ConnectionError
Bad response from PING health check
Error message
Bad response from PING health check
What it means
Raised by _send_ping (connection.py:1317-1321) during a periodic health check when PING is sent but the response is not 'PONG'. This indicates the connection is in a corrupt/half-open state where data can still be read but it is not a valid Redis reply (e.g. mid-protocol garbage, a proxy returning an HTTP error, or a connection that returned an unexpected frame). check_health() runs it through retry.call_with_retry; on final failure _ping_failed disconnects with health_check_failed=True.
Solutions
- Set socket_keepalive=True and a sensible socket_timeout to detect dead sockets earlier.
- Reduce health_check_interval to catch bad connections faster.
- Ensure nothing but Redis is answering on the target host:port (remove HTTP/TLS-terminating proxies on the Redis port).
- Configure retry_on_timeout=True / a Retry policy so transient corruption triggers reconnect.
- Investigate RESP desync: enable debug logging of the parser and check for earlier unhandled responses.
Example fix
# before
r = redis.Redis(host=h, port=p)
# after
r = redis.Redis(
host=h, port=p,
health_check_interval=30,
socket_keepalive=True,
socket_timeout=5,
retry_on_timeout=True,
) Defensive patterns
Strategy: retry
Validate before calling
# Configure resilient health-check defaults up front
r = redis.Redis(
host=h, port=p,
health_check_interval=30, # detect dead sockets
socket_keepalive=True,
socket_timeout=5,
retry_on_timeout=True,
retry=Retry(ExponentialBackoff(), 3),
) Type guard
null
Try / catch
from redis.exceptions import ConnectionError
for _ in range(3):
try:
r.get('k')
break
except ConnectionError as e:
if 'PING health check' in str(e):
continue # pool reopens; retry idempotent op
raise Prevention
- Always set health_check_interval + socket_keepalive for long-lived clients.
- Confirm nothing but Redis answers on the port (no HTTP/TLS proxies).
- Use the connection pool so bad sockets are replaced automatically.
- Investigate RESP desync if PONG corruption recurs.
When it happens
Trigger: health_check_interval is set (>0) and the established socket returns something other than PONG to PING — corrupted buffer, an intermediary returning an error page, a stale connection after a silent server-side close, or a protocol desync from a prior malformed exchange.
Common situations: Long-idle connections behind NAT/firewalls that silently drop; a proxy returning a 502/HTML body on the Redis port; RESP parser desync after an earlier unhandled error; network equipment mangling the byte stream.
Related errors
- A non health check response was cleaned by execute_command
- Bad response from PING health check
- Connection closed by server.
- Connection closed by server.
- Connection closed by server.
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/c338869636befef1.
Report an issue: GitHub.
Appendix: source
Thrown at redis/connection.py:1321
self.maintenance_state = MaintenanceState.NONE
# reset the sets that keep track of received start maint
# notifications and skipped end maint notifications
self.reset_received_notifications()
def mark_for_reconnect(self):
self._should_reconnect = True
def should_reconnect(self):
return self._should_reconnect
def reset_should_reconnect(self):
self._should_reconnect = False
def _send_ping(self):
"""Send PING, expect PONG in return"""
self.send_command("PING", check_health=False)
if str_if_bytes(self.read_response()) != "PONG":
raise ConnectionError("Bad response from PING health check")
def _ping_failed(self, error, failure_count):
"""Function to call when PING fails"""
self.disconnect(
error=error, failure_count=failure_count, health_check_failed=True
)
def check_health(self):
"""Check the health of the connection with a PING/PONG"""
if self.health_check_interval and time.monotonic() > self.next_health_check:
self.retry.call_with_retry(
self._send_ping,
self._ping_failed,
with_failure_count=True,
)
def send_packed_command(self, command, check_health=True):
"""Send an already packed command to the Redis server"""View on GitHub (pinned to 6a6b581b48)