redis/redis-py · critical · InvalidResponse

Protocol Error: {raw!r}

Error message

Protocol Error: {raw!r}

What it means

Raised by the sync RESP3 parser when the first byte of a reply line matches none of RESP3's type markers (-, !, +, _, :, (, ,, #, $, =, *, ~, %, >). Because RESP3 accepts more types than RESP2, hitting this is an even stronger signal of genuine wire corruption or a protocol-version mismatch (e.g. the server didn't actually upgrade to RESP3, or sent RESP2 framing). redis.exceptions.InvalidResponse; the raw bytes are included.

Source

Thrown at redis/_parsers/resp3.py:158

                self._read_response(
                    disable_decoding=disable_decoding,
                    push_request=push_request,
                    timeout=timeout,
                )
                for _ in range(int(response))
            ]
            response = self.handle_push_response(response)

            # if this is a push request return the push response
            if push_request:
                return response

            return self._read_response(
                disable_decoding=disable_decoding,
                push_request=push_request,
            )
        else:
            raise InvalidResponse(f"Protocol Error: {raw!r}")

        if isinstance(response, bytes) and disable_decoding is False:
            response = self.encoder.decode(response)

        return response


class _AsyncRESP3Parser(_AsyncRESPBase, AsyncPushNotificationsParser):
    def __init__(self, socket_read_size):
        super().__init__(socket_read_size)
        self.pubsub_push_handler_func = self.handle_pubsub_push_response
        self.invalidation_push_handler_func = None

    async def handle_pubsub_push_response(self, response):
        logger = getLogger("push_response")
        logger.debug("Push response: %s", response)
        return response

View on GitHub (pinned to 6a6b581b48)

Solutions

  1. Let the client negotiate the protocol (omit protocol=) instead of forcing protocol=3 against an old server.
  2. Confirm the server version supports RESP3 (Redis >= 6).
  3. Verify the endpoint is Redis and that TLS matches.
  4. Remove non-RESP proxies.
  5. For RESP2-only servers, set protocol=2.

Example fix

# before - forcing RESP3 against an old server
r = redis.Redis(host='oldredis', protocol=3)  # server is Redis 5 -> InvalidResponse: Protocol Error

# after - match protocol to the server (or upgrade the server to Redis >= 6)
r = redis.Redis(host='oldredis', protocol=2)
# or: r = redis.Redis(host='redis6', protocol=3)
Defensive patterns

Strategy: validation

Validate before calling

# Probe the server's RESP capabilities before locking in protocol=3
import socket

def server_supports_resp3(host, port, timeout=2):
    s = socket.create_connection((host, port), timeout)
    try:
        s.sendall(b'*2\r\n$4\r\nHELLO\r\n$1\r\n3\r\n')
        first = s.recv(1)
        return first in (b'%', b'-')  # map reply (ok) or -NOPROTO (not supported)
    finally:
        s.close()

Type guard

from redis.exceptions import InvalidResponse

def is_resp3_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'Not valid RESP3 - check server version/protocol/TLS: {e}') from e

Prevention

When it happens

Trigger: Server that ignored HELLO 3 and stays on RESP2 while the client expects RESP3 types; a Redis < 6 (no RESP3) with protocol=3 forced; a proxy that strips HELLO; a custom module returning an unsupported RESP type; wrong endpoint / non-RESP service; TLS/plaintext mismatch; buffer corruption from a prior interrupted read.

Common situations: Talking to Redis 5 (or older) with protocol=3 forced; an old RESP2-only Redis behind a new client default (RESP3 is the default on the wire now); a module returning a type the parser doesn't recognize; a corrupting proxy.

Related errors


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