redis/redis-py · error · ConnectionError

protocol must be either 2 or 3

Error message

protocol must be either 2 or 3

What it means

Raised in AbstractConnection.__init__ (connection.py:928-929) after int(protocol) succeeds but the value is outside the supported range [2,3]. redis-py only speaks RESP2 and RESP3, so any other integer (1, 4, 0, -1) is rejected. This guards against silently negotiating an unsupported wire version.

Solutions

  1. Use protocol=2 or protocol=3 (3 is the current default).
  2. Validate config at load time: if proto not in (2,3): raise/bound to 3.
  3. Upgrade redis-py if you genuinely need a newer RESP version that may have shipped.

Example fix

# before
r = redis.Redis(host=h, port=p, protocol=4)
# after
r = redis.Redis(host=h, port=p, protocol=3)
Defensive patterns

Strategy: validation

Validate before calling

PROTO = int(proto) if proto is not None else 3
assert PROTO in (2, 3), f'protocol must be 2 or 3, got {PROTO}'
r = redis.Redis(host=h, port=p, protocol=PROTO)

Type guard

def is_supported_resp(p) -> bool:
    try:
        return int(p) in (2, 3)
    except (TypeError, ValueError):
        return False

Try / catch

from redis.exceptions import ConnectionError
try:
    r = redis.Redis(host=h, port=p, protocol=proto)
except ConnectionError as e:
    if 'either 2 or 3' in str(e):
        r = redis.Redis(host=h, port=p, protocol=3)
    else:
        raise

Prevention

When it happens

Trigger: Passing protocol=1, protocol=4, protocol=0, or any integer not equal to 2 or 3 to redis.Redis/Connection.

Common situations: Typo (protocol=4); arithmetic/config drift producing an out-of-range number; assuming a newer RESP version exists before upgrading the library.

Related errors


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

Appendix: source

Thrown at redis/connection.py:929

        self.health_check_interval = health_check_interval
        self.next_health_check = 0
        self.redis_connect_func = redis_connect_func
        self.encoder = Encoder(encoding, encoding_errors, decode_responses)
        self.handshake_metadata = None
        self._sock = None
        self._socket_read_size = socket_read_size
        self._connect_callbacks = []
        self._buffer_cutoff = 6000
        self._re_auth_token: Optional[TokenInterface] = None
        try:
            p = int(protocol)
        except TypeError:
            p = DEFAULT_RESP_VERSION
        except ValueError:
            raise ConnectionError("protocol must be an integer")
        else:
            if p < 2 or p > 3:
                raise ConnectionError("protocol must be either 2 or 3")
        self.protocol = p
        self.legacy_responses = legacy_responses
        if self.protocol == 3 and parser_class == _RESP2Parser:
            # If the protocol is 3 but the parser is RESP2, change it to RESP3
            # This is needed because the parser might be set before the protocol
            # or might be provided as a kwarg to the constructor
            # We need to react on discrepancy only for RESP2 and RESP3
            # as hiredis supports both
            parser_class = _RESP3Parser
        self.set_parser(parser_class)

        self._command_packer = self._construct_command_packer(command_packer)
        self._should_reconnect = False

        # HIMPORT client-side state. `himport_registry` is the shared client-level
        # registry (empty if unconfigured) and persists across reconnects.
        self.himport_registry = himport_registry
        self._reset_himport_state()

View on GitHub (pinned to 6a6b581b48)