redis/redis-py · warning · ConnectionError

Connection has data

Error message

Connection has data

What it means

Raised by ensure_connection() when a pooled connection returned to the caller still has unread data on the socket (can_read() is True) and maintenance notifications are not enabled. A healthy pooled connection should be drained; leftover bytes mean a previous response was not fully read (or the socket is in a bad state), so the pool treats it as corrupt and refuses to hand it out.

Source

Thrown at redis/asyncio/connection.py:2873

            decode_responses=kwargs.get("decode_responses", False),
        )

    def make_connection(self):
        """Create a new connection.  Can be overridden by child classes."""
        # 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)

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Always fully consume command responses before the connection is released back to the pool (let the client API manage release).
  2. Guard against task cancellation around commands (use asyncio.shield or structured concurrency) so a response is not left half-read.
  3. If it recurs, set a lower socket timeout or enable health-check reconnection; report a parser bug if it persists with stock usage.

Example fix

// before
conn = await pool.get_connection()
await conn.send_command('GET', 'k')
# task cancelled before reading response -> conn returned with data
// after
conn = await pool.get_connection()
try:
    await conn.send_command('GET', 'k')
    resp = await conn.read_response()
finally:
    await pool.release(conn)
Defensive patterns

Strategy: try-catch

Validate before calling

# prefer the high-level API which manages release/drain for you
async with redis.client('GET', 'k') as r:
    val = await r

Type guard

def connection_is_clean(conn) -> bool:
    import asyncio
    try:
        return not conn.can_read() if hasattr(conn, 'can_read') else True
    except Exception:
        return False

Try / catch

from redis.exceptions import ConnectionError
try:
    await pool.get_connection('GET')
except ConnectionError as e:
    if 'Connection has data' in str(e):
        await pool.disconnect()
        # retry; pool will build a fresh connection

Prevention

When it happens

Trigger: A connection is returned to the pool before its previous response was fully consumed (e.g., an interrupted/partial read, a cancelled task mid-command), then retrieved again and checked in ensure_connection(). The first can_read() check at connection.py:2872 trips it.

Common situations: Task cancellation in the middle of reading a response; a custom command path that returns the connection early; protocol desync after a network blip; rarely, a parser bug.

Related errors


AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04). Data as JSON: /data/errors/cd7147f89f16e38d.json. Report an issue: GitHub.