redis/redis-py · error · OSError
Buffer is closed.
Error message
Buffer is closed.
What it means
Raised by the async hiredis parser's can_read() when self._connected is False: on_disconnect() ran (e.g. the asyncio task was cancelled or another task closed the connection), so the parser's buffer is closed. Notably this is raised as the builtin OSError, NOT redis.exceptions.ConnectionError - so a 'except ConnectionError' handler will miss it.
Source
Thrown at redis/_parsers/hiredis.py:325
)
except AttributeError:
# hiredis < 3.2
self._hiredis_PushNotificationType = None
def on_disconnect(self):
self._connected = False
@deprecated_function(
version="8.0.0", reason="Use can_read() instead", name="can_read_destructive"
)
async def can_read_destructive(self) -> bool:
return await self.can_read()
async def can_read(self) -> bool:
# TODO: Rename this API; it detects pending data or dirty/closed
# connection state, not only whether application data can be read.
if not self._connected:
raise OSError("Buffer is closed.")
# EOF means the connection is closed and not safe to reuse.
if self._reader.has_data() or self._stream.at_eof():
return True
# asyncio.StreamReader has no public non-destructive API for checking
# buffered bytes. Preserve dirty-connection detection for hiredis; tests
# with a real StreamReader guard this private buffer API in CI.
return bool(self._stream._buffer)
async def read_from_socket(self):
buffer = await self._stream.read(self._read_size)
if not buffer or not isinstance(buffer, bytes):
raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR) from None
self._reader.feed(buffer)
# data was read from the socket and added to the buffer.
# return True to indicate that data was read.
return True
async def read_response(View on GitHub (pinned to 6a6b581b48)
Solutions
- Treat a closed parser as 'no data'; catch OSError around can_read().
- Let the connection pool hand out fresh connections rather than probing dead ones.
- Guard pubsub loops with a flag set on disconnect/cancel.
- Check the connection's connected state before probing.
Example fix
# before
ready = await pubsub.connection.can_read() # after disconnect -> OSError: Buffer is closed.
# after - guard the probe and treat closed as 'no data'
try:
ready = await pubsub.connection.can_read()
except OSError:
ready = False # connection gone; reconnect Defensive patterns
Strategy: try-catch
Validate before calling
# Track lifecycle so you don't probe a closed parser
async def safe_can_read(conn):
if not getattr(conn, '_connected', False):
return False
try:
return await conn.can_read()
except OSError:
return False Type guard
def is_closed_buffer(e: BaseException) -> bool:
return isinstance(e, OSError) and 'buffer is closed' in str(e).lower() Try / catch
try:
ready = await conn.can_read()
except OSError:
ready = False Prevention
- This surfaces as OSError, not redis.exceptions.ConnectionError - your 'except ConnectionError' will miss it.
- Set a 'closing' event when cancelling pubsub/long-running tasks so readiness probes short-circuit.
When it happens
Trigger: Calling can_read() on an async connection after it was disconnected: pubsub readiness checks, health checks, or manual probing of a connection whose owning task was cancelled / whose client was closed, or after the pool disconnected it.
Common situations: Cancelling an asyncio pubsub task and then probing its connection; probing a connection after asyncio cancellation propagated through on_disconnect; a shared connection used after close().
Related errors
- Buffer is closed.
- Connection closed by server.
- Hiredis is not available.
- Error {err_no} while writing to socket. {errmsg}.
- Error while reading from {host_error}: {e.args}
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/5e529c5e9fbbab90.
Report an issue: GitHub.