redis/redis-py · error · PubSubError
A non health check response was cleaned by execute_command
Error message
A non health check response was cleaned by execute_command: {response} What it means
Raised by PubSub.clean_health_check_responses() when, while draining queued socket responses that were expected to be health-check PING replies, the library reads a response that is NOT a recognized health-check response. It indicates the pubsub connection's response stream is out of sync with the library's bookkeeping (health_check_response_counter), so a real pubsub message or unexpected protocol frame was consumed as if it were a health check.
Solutions
- Avoid issuing direct commands on a PubSub object that is actively subscribed; use a separate client for command traffic.
- If you must send commands, ensure no health-check PINGs are in flight (raise health_check_interval or call check_health at controlled points).
- Upgrade redis-py to the latest patch release — response-stream desync bugs in this drain loop have been fixed over time.
- If reproducing, capture the exact `response` value in the error message to identify which non-health-check frame is being read.
- As a last resort, lower socket_timeout or disable health_check_interval (set to 0) on the connection used for pubsub.
Example fix
// before
pubsub = r.pubsub()
pubsub.subscribe('ch')
pubsub.execute_command('GET', 'key') # interleaves with health checks
// after
pubsub = r.pubsub()
pubsub.subscribe('ch')
# use a separate client for command traffic
value = r.get('key') Defensive patterns
Strategy: try-catch
Validate before calling
# Before issuing any command on a pubsub connection, confirm no health-check
# responses are pending and the connection is subscribed cleanly.
if pubsub.health_check_response_counter > 0:
pubsub.clean_health_check_responses() # may raise PubSubError
# Prefer not to share the pubsub connection for command traffic at all. Type guard
def is_safe_to_send_on_pubsub(pubsub) -> bool:
return (
pubsub.connection is not None
and pubsub.health_check_response_counter == 0
) Try / catch
from redis.exceptions import PubSubError
try:
pubsub.clean_health_check_responses()
except PubSubError as e:
# response stream is desynced; reconnect and resubscribe
pubsub.close()
pubsub = client.pubsub()
pubsub.subscribe('channel') Prevention
- Use a dedicated client for commands; reserve the PubSub connection for subscriptions.
- Keep health_check_interval modest so PINGs do not pile up under load.
- Treat any PubSubError from the drain loop as a signal to reconnect and resubscribe.
When it happens
Trigger: Calling PubSub.execute_command (or any command sent over a subscribed pubsub connection) while health_check_response_counter > 0, or interleaving direct commands with pending health-check PINGs on a subscribed connection. Concretely: a subscribed PubSub where conn.health_check_interval is set and a real message arrives between PING send and the clean_health_check_responses() drain loop in _execute.
Common situations: A long-lived pubsub listener with a low health_check_interval under heavy message load; sending raw commands on a PubSub object that is also receiving messages; RESP2/RESP3 protocol mismatches that change the shape of the PING reply; bugs in custom retry logic that re-issue PINGs.
Related errors
- Bad response from PING health check
- Bad response from PING health check
- Buffer is closed.
- Connection has data
- Connection not ready
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/03998d9e4dd1fccc.
Report an issue: GitHub.
Appendix: source
Thrown at redis/client.py:1247
kwargs = {"check_health": not self.subscribed}
if not self.subscribed:
self.clean_health_check_responses()
with self._lock:
self._execute(connection, connection.send_command, *args, **kwargs)
def clean_health_check_responses(self) -> None:
"""
If any health check responses are present, clean them
"""
ttl = 10
conn = self.connection
while conn and self.health_check_response_counter > 0 and ttl > 0:
if self._execute(conn, conn.can_read, timeout=conn.socket_timeout):
response = self._execute(conn, conn.read_response)
if self.is_health_check_response(response):
self.health_check_response_counter -= 1
else:
raise PubSubError(
"A non health check response was cleaned by "
"execute_command: {}".format(response)
)
ttl -= 1
def _reconnect(
self,
conn,
error: Optional[Exception] = None,
failure_count: Optional[int] = None,
start_time: Optional[float] = None,
command_name: Optional[str] = None,
) -> None:
"""
The supported exceptions are already checked in the
retry object so we don't need to do it here.
In this error handler we are trying to reconnect to the server.View on GitHub (pinned to 6a6b581b48)