redis/redis-py · error · ConnectionError
protocol must be an integer
Error message
protocol must be an integer
What it means
Raised in AbstractConnection.__init__ (connection.py:921-926) when int(protocol) raises ValueError — i.e. protocol was given as a non-integer, non-numeric string (e.g. 'RESP3', 'three', None handled separately). The protocol argument selects the RESP wire version and must coerce to an integer; non-numeric input is a configuration bug. It surfaces as a ConnectionError at connection start.
Solutions
- Pass an integer or numeric string: protocol=3 or protocol='3'.
- If loading from config, coerce and validate first: protocol=int(str(proto).strip()).
- Accept only the symbolic values upstream and map them: {'RESP2':2,'RESP3':3}.
- Leave protocol unset to use the library default (DEFAULT_RESP_VERSION).
Example fix
# before r = redis.Redis(host=h, port=p, protocol='RESP3') # after r = redis.Redis(host=h, port=p, protocol=3)
Defensive patterns
Strategy: validation
Validate before calling
def resolve_protocol(raw):
if raw is None:
return None # library default
try:
p = int(raw)
except (TypeError, ValueError):
raise ValueError(f'protocol must be a number, got {raw!r}') from None
if p not in (2, 3):
raise ValueError(f'protocol must be 2 or 3, got {p}')
return p
r = redis.Redis(host=h, port=p, protocol=resolve_protocol(os.environ.get('REDIS_PROTOCOL'))) Type guard
from typing import Any
def is_valid_protocol(v: Any) -> bool:
try:
return int(v) in (2, 3)
except (TypeError, ValueError):
return False Try / catch
from redis.exceptions import ConnectionError
try:
r = redis.Redis(host=h, port=p, protocol=raw_proto)
r.ping()
except ConnectionError as e:
if 'protocol must be' in str(e):
r = redis.Redis(host=h, port=p, protocol=3) # safe default
else:
raise Prevention
- Coerce and validate protocol from config/env before passing it in.
- Map symbolic names ('RESP2'/'RESP3') to ints at the config boundary.
- Leave protocol unset to use the library default when unsure.
When it happens
Trigger: Passing protocol='RESP3', protocol='three', protocol=[3], or any non-numeric value to redis.Redis/Connection. Note int('abc') raises ValueError -> this error; int(None)/int(['3']) raise TypeError -> silently fall back to DEFAULT_RESP_VERSION (so those do NOT hit this message).
Common situations: Reading protocol from an env var or YAML as a string like 'RESP3'; UI/config form storing the symbolic name instead of the number; misunderstanding that protocol expects 2 or 3, not the RESP name.
Related errors
- protocol must be either 2 or 3
- Cache must implement CacheInterface
- Client caching is only supported with RESP version 3
- Error setting client name
- Invalid Database
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/ec53675de88cce77.
Report an issue: GitHub.
Appendix: source
Thrown at redis/connection.py:926
self.retry.update_supported_errors(self.retry_on_error)
else:
self.retry = Retry(NoBackoff(), 0)
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-levelView on GitHub (pinned to 6a6b581b48)