redis/redis-py · error · OSError

Buffer is closed.

Error message

Buffer is closed.

What it means

Raised in _AsyncRESPBase.can_read() (redis/_parsers/base.py:549) and the async hiredis parser's can_read() when self._connected is False. It signals that the parser's internal buffer/stream has been torn down by on_disconnect(), so a readiness check cannot proceed. The literal raised is OSError('Buffer is closed.').

Solutions

  1. Catch OSError('Buffer is closed.') and treat it as 'connection must be re-established', then reconnect/resubscribe.
  2. Track connection liveness yourself and stop polling once disconnected, instead of relying on can_read() to fail.
  3. Pull a fresh connection from the pool rather than reusing the disconnected one.
  4. For pubsub, use the high-level pubsub() API with get_message(ignore_subscribe_messages=True) and handle None/reconnect rather than probing raw connections.

Example fix

# before
while True:
    msg = await pubsub.get_message()  # may hit 'Buffer is closed.' on a dead conn

# after
try:
    msg = await pubsub.get_message()
except OSError:
    await pubsub.close()
    pubsub = r.pubsub()
    await pubsub.subscribe("ch")
    continue
Defensive patterns

Strategy: try-catch

Validate before calling

# Track pubsub/connection liveness yourself instead of probing a dead connection
if not getattr(parser, "_connected", False):
    # skip the probe; reconnect instead
    pass

Try / catch

try:
    msg = await pubsub.get_message()
except OSError:
    # 'Buffer is closed.' -> connection torn down
    await pubsub.close()
    pubsub = r.pubsub()
    await pubsub.subscribe("ch")

Prevention

When it happens

Trigger: Calling can_read() (directly or via pubsub/health-check/connection-pool readiness probing) on an async connection that already had on_disconnect() invoked. Typical in pubsub get_message loops, keepalive checks, or failover paths that probe a dropped connection.

Common situations: A pubsub loop continuing to poll after the server dropped the connection; reusing a connection object after an explicit disconnect; failover scenarios where the old node is probed after being marked dead.

Related errors


AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10). Data as JSON: /api/errors/4b1bb8b9a9c8ff7e. Report an issue: GitHub.

Appendix: source

Thrown at redis/_parsers/base.py:549

        self._connected = True

    def on_disconnect(self):
        """Called when the stream disconnects"""
        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.")
        if self._buffer:
            return True
        # asyncio.StreamReader has no public non-destructive API for checking
        # buffered bytes. Preserve dirty-connection detection for the Python
        # parser and fail loudly if the private buffer API changes.
        return bool(self._stream._buffer) or self._stream.at_eof()

    async def _read(self, length: int) -> bytes:
        """
        Read `length` bytes of data.  These are assumed to be followed
        by a '\r\n' terminator which is subsequently discarded.
        """
        want = length + 2
        end = self._pos + want
        if len(self._buffer) >= end:
            result = self._buffer[self._pos : end - 2]
        else:
            tail = self._buffer[self._pos :]

View on GitHub (pinned to 6a6b581b48)