redis/redis-py · critical · InvalidResponse

Protocol Error: {raw!r}

Error message

Protocol Error: {raw!r}

What it means

Raised by the sync RESP2 parser when the first byte of a reply line is none of -, +, :, $, *: the bytes on the wire are not valid RESP2 at all. redis.exceptions.InvalidResponse (a RedisError); the raw bytes are included so you can see what arrived. This almost always means misconfiguration or stream corruption, not a transient fault.

Source

Thrown at redis/_parsers/resp2.py:71

            pass
        # int value
        elif byte == b":":
            return int(response)
        # bulk response
        elif byte == b"$" and response == b"-1":
            return None
        elif byte == b"$":
            response = self._buffer.read(int(response), timeout=timeout)
        # multi-bulk response
        elif byte == b"*" and response == b"-1":
            return None
        elif byte == b"*":
            response = [
                self._read_response(disable_decoding=disable_decoding, timeout=timeout)
                for i in range(int(response))
            ]
        else:
            raise InvalidResponse(f"Protocol Error: {raw!r}")

        if disable_decoding is False:
            response = self.encoder.decode(response)
        return response


class _AsyncRESP2Parser(_AsyncRESPBase):
    """Async class for the RESP2 protocol"""

    async def read_response(self, disable_decoding: bool = False):
        if not self._connected:
            raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)
        if self._chunks:
            # augment parsing buffer with previously read data
            self._buffer += b"".join(self._chunks)
            self._chunks.clear()
        self._pos = 0
        response = await self._read_response(disable_decoding=disable_decoding)

View on GitHub (pinned to 6a6b581b48)

Solutions

  1. Confirm the host:port is actually Redis: redis-cli -h ... -p ... PING.
  2. Verify TLS matches the endpoint: use rediss:// (or ssl=True) for TLS servers, redis:// for plain.
  3. Remove any non-RESP proxy between client and Redis.
  4. If using stunnel/tunneling, confirm it forwards to the Redis port unchanged.
  5. Re-check decode_responses/encoding and the protocol setting.

Example fix

# before - pointed at the wrong service (Postgres!)
r = redis.Redis(host='db', port=5432)
r.get('k')  # -> InvalidResponse: Protocol Error: b'E'

# after
r = redis.Redis(host='redis', port=6379)
r.get('k')

# TLS fix: use rediss:// for TLS endpoints
r = redis.Redis.from_url('rediss://redis.example:6379', ssl_cert_reqs='required')
Defensive patterns

Strategy: validation

Validate before calling

# Cheap preflight: confirm the endpoint speaks RESP before relying on it
import socket

def is_resp_endpoint(host, port, timeout=2):
    s = socket.create_connection((host, port), timeout)
    try:
        s.sendall(b'*1\r\n$4\r\nPING\r\n')
        return s.recv(8).startswith(b'+PONG')
    finally:
        s.close()

Type guard

from redis.exceptions import InvalidResponse

def is_protocol_error(e: BaseException) -> bool:
    return isinstance(e, InvalidResponse) and str(e).startswith('Protocol Error')

Try / catch

from redis.exceptions import InvalidResponse
try:
    r.get('k')
except InvalidResponse as e:
    raise SystemExit(f'Wire is not RESP - check host/TLS/proxy: {e}') from e

Prevention

When it happens

Trigger: Connecting the client to something that doesn't speak RESP (wrong port/service); TLS/plain mismatch (TLS client to a plain port, or plain client to a TLS-only port - you often see the TLS handshake bytes as the 'raw'); a corrupting proxy/load balancer; a half-read buffer left after a previous interrupted command; a RESP3-only type sent to a protocol=2 parser.

Common situations: Pointing redis-py at a MySQL/Postgres/memcached port; stunnel/TLS misconfiguration (redis:// to a rediss:// endpoint); an HTTP proxy returning 'HTTP/1.1 400' as the first line; a sidecar that injects non-RESP bytes.

Related errors


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