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
- Confirm the host:port is actually Redis: redis-cli -h ... -p ... PING.
- Verify TLS matches the endpoint: use rediss:// (or ssl=True) for TLS servers, redis:// for plain.
- Remove any non-RESP proxy between client and Redis.
- If using stunnel/tunneling, confirm it forwards to the Redis port unchanged.
- 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
- InvalidResponse is NOT transient - do not retry blindly; it indicates a wrong endpoint, TLS/plaintext mismatch, or a corrupting proxy.
- Use rediss:// for TLS endpoints and ssl_cert_reqs='required' in production; plaintext-to-TLS-port produces a Protocol Error.
- If you see HTTP/HTML in the raw bytes, you're pointed at an HTTP service or proxy, not Redis.
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
- Protocol Error: {raw!r}
- Wrong number of response items from pipeline execution
- Hiredis is not installed
- Hiredis is not available.
- Connection closed by server.
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/980a70f94f1eade3.
Report an issue: GitHub.