{"record":{"id":"cf50f5fc2c4f0203","repo":"redis/redis-py","slug":"protocol-error-raw-r-cf50f5","errorCode":null,"errorMessage":"Protocol Error: {raw!r}","messagePattern":"Protocol Error: (.+?)","errorType":"exception","errorClass":"InvalidResponse","httpStatus":null,"severity":"critical","filePath":"redis/_parsers/resp3.py","lineNumber":158,"sourceCode":"                self._read_response(\n                    disable_decoding=disable_decoding,\n                    push_request=push_request,\n                    timeout=timeout,\n                )\n                for _ in range(int(response))\n            ]\n            response = self.handle_push_response(response)\n\n            # if this is a push request return the push response\n            if push_request:\n                return response\n\n            return self._read_response(\n                disable_decoding=disable_decoding,\n                push_request=push_request,\n            )\n        else:\n            raise InvalidResponse(f\"Protocol Error: {raw!r}\")\n\n        if isinstance(response, bytes) and disable_decoding is False:\n            response = self.encoder.decode(response)\n\n        return response\n\n\nclass _AsyncRESP3Parser(_AsyncRESPBase, AsyncPushNotificationsParser):\n    def __init__(self, socket_read_size):\n        super().__init__(socket_read_size)\n        self.pubsub_push_handler_func = self.handle_pubsub_push_response\n        self.invalidation_push_handler_func = None\n\n    async def handle_pubsub_push_response(self, response):\n        logger = getLogger(\"push_response\")\n        logger.debug(\"Push response: %s\", response)\n        return response\n","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/_parsers/resp3.py#L140-L176","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Let the client negotiate the protocol (omit protocol=) instead of forcing protocol=3 against an old server.","Confirm the server version supports RESP3 (Redis >= 6).","Verify the endpoint is Redis and that TLS matches.","Remove non-RESP proxies.","For RESP2-only servers, set protocol=2."],"exampleFix":"# before - forcing RESP3 against an old server\nr = redis.Redis(host='oldredis', protocol=3)  # server is Redis 5 -> InvalidResponse: Protocol Error\n\n# after - match protocol to the server (or upgrade the server to Redis >= 6)\nr = redis.Redis(host='oldredis', protocol=2)\n# or: r = redis.Redis(host='redis6', protocol=3)","handlingStrategy":"validation","validationCode":"# Probe the server's RESP capabilities before locking in protocol=3\nimport socket\n\ndef server_supports_resp3(host, port, timeout=2):\n    s = socket.create_connection((host, port), timeout)\n    try:\n        s.sendall(b'*2\\r\\n$4\\r\\nHELLO\\r\\n$1\\r\\n3\\r\\n')\n        first = s.recv(1)\n        return first in (b'%', b'-')  # map reply (ok) or -NOPROTO (not supported)\n    finally:\n        s.close()","typeGuard":"from redis.exceptions import InvalidResponse\n\ndef is_resp3_protocol_error(e: BaseException) -> bool:\n    return isinstance(e, InvalidResponse) and str(e).startswith('Protocol Error')","tryCatchPattern":"from redis.exceptions import InvalidResponse\ntry:\n    r.get('k')\nexcept InvalidResponse as e:\n    raise SystemExit(f'Not valid RESP3 - check server version/protocol/TLS: {e}') from e","preventionTips":["Don't force protocol=3 on Redis < 6; let the client negotiate or set protocol=2.","InvalidResponse here usually means a HELLO/RESP3 mismatch or a non-RESP endpoint - not a transient fault; don't retry."],"tags":["protocol","resp3","version","config","invalid-response","sync"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}