redis/redis-py · error · ConnectionError
Connection not ready
Error message
Connection not ready
What it means
After 'Connection has data', ensure_connection disconnects and reconnects, then re-checks can_read(). If data is STILL present on a freshly connected socket (and maintenance notifications are not enabled), it raises ConnectionError('Connection not ready'). This indicates the server (or an intermediary) is injecting data immediately on connect, which the client cannot safely ignore.
Solutions
- If using RESP3 with server pushes, ensure maintenance notifications / push handling is configured so the data check is skipped.
- Retry the operation once (the failure can be transient).
- Check for a proxy that prepends data and verify parser/wire-protocol compatibility.
Defensive patterns
Strategy: retry
Try / catch
from redis.exceptions import ConnectionError
for attempt in range(retries):
try:
return await client.get(key)
except ConnectionError as e:
if 'Connection not ready' not in str(e):
raise
await asyncio.sleep(backoff(attempt))
raise Prevention
- If using RESP3 pushes, enable maintenance notifications so the data check is bypassed.
- Investigate proxies that inject bytes on connect if this recurs.
When it happens
Trigger: A reconnect in ensure_connection succeeds but can_read() is true again right away, e.g. RESP3 push messages arriving before HELLO completes, a proxy prepending bytes, or server keepalive/monitor noise.
Common situations: RESP3 server pushes not being consumed; a TCP proxy or sidecar injecting data; incompatible parser; server-side MONITOR/keepalive.
Related errors
- Connection has data
- Bad response from PING health check
- A non health check response was cleaned by execute_command
- Buffer is closed.
- Cannot enable maintenance notifications for connection…
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/94dff8eb4da265ba.
Report an issue: GitHub.
Appendix: source
Thrown at redis/asyncio/connection.py:2881
# Note: We don't record IDLE here because async uses a sync make_connection
# but async record_connection_count. The recording is handled in get_connection.
return self.connection_class(**self.connection_kwargs)
async def ensure_connection(self, connection: AbstractConnection):
"""Ensure that the connection object is connected and valid"""
await connection.connect()
# connections that the pool provides should be ready to send
# a command. if not, the connection was either returned to the
# pool before all data has been read or the socket has been
# closed. either way, reconnect and verify everything is good.
try:
if await connection.can_read() and not self.maint_notifications_enabled():
raise ConnectionError("Connection has data") from None
except (ConnectionError, TimeoutError, OSError):
await connection.disconnect()
await connection.connect()
if await connection.can_read() and not self.maint_notifications_enabled():
raise ConnectionError("Connection not ready") from None
async def release(self, connection: AbstractConnection):
"""Releases the connection back to the pool"""
# Connections should always be returned to the correct pool,
# not doing so is an error that will cause an exception here.
async with self._lock:
self._in_use_connections.remove(connection)
if connection.should_reconnect():
await connection.disconnect()
self._available_connections.append(connection)
await self._event_dispatcher.dispatch_async(
AsyncAfterConnectionReleasedEvent(connection)
)
# Record state transition: USED -> IDLEView on GitHub (pinned to 6a6b581b48)