redis/redis-py · error · RedisError

Invalid SSL Certificate Requirements Flag: {cert_reqs}

Error message

Invalid SSL Certificate Requirements Flag: {cert_reqs}

What it means

Raised as a RedisError from RedisSSLContext.__init__ when ssl_cert_reqs is passed as a string that is not one of 'none', 'optional', 'required'. These three map to ssl.CERT_NONE / CERT_OPTIONAL / CERT_REQUIRED; any other string is rejected because it cannot be translated to a VerifyMode. Numeric ssl constants pass through unchecked.

Source

Thrown at redis/asyncio/connection.py:1641

        min_version: Optional[TLSVersion] = None,
        ciphers: Optional[str] = None,
        password: Optional[str] = None,
    ):
        if not SSL_AVAILABLE:
            raise RedisError("Python wasn't built with SSL support")

        self.keyfile = keyfile
        self.certfile = certfile
        if cert_reqs is None:
            cert_reqs = ssl.CERT_NONE
        elif isinstance(cert_reqs, str):
            CERT_REQS = {  # noqa: N806
                "none": ssl.CERT_NONE,
                "optional": ssl.CERT_OPTIONAL,
                "required": ssl.CERT_REQUIRED,
            }
            if cert_reqs not in CERT_REQS:
                raise RedisError(
                    f"Invalid SSL Certificate Requirements Flag: {cert_reqs}"
                )
            cert_reqs = CERT_REQS[cert_reqs]
        self.cert_reqs = cert_reqs
        self.include_verify_flags = include_verify_flags
        self.exclude_verify_flags = exclude_verify_flags
        self.ca_certs = ca_certs
        self.ca_data = ca_data
        self.ca_path = ca_path
        self.check_hostname = (
            check_hostname if self.cert_reqs != ssl.CERT_NONE else False
        )
        self.min_version = min_version
        self.ciphers = ciphers
        self.password = password
        self.context: Optional[SSLContext] = None

    def get(self) -> SSLContext:

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Use one of the exact lowercase strings: 'none', 'optional', or 'required'.
  2. Or pass the ssl constant directly: ssl.CERT_REQUIRED, ssl.CERT_OPTIONAL, ssl.CERT_NONE.
  3. Double-check URL query strings: rediss://host?ssl_cert_reqs=required (lowercase).

Example fix

// before
r = redis.asyncio.Redis(host=h, port=p, ssl=True, ssl_cert_reqs="CERT_REQUIRED")

// after
import ssl
r = redis.asyncio.Redis(host=h, port=p, ssl=True, ssl_cert_reqs=ssl.CERT_REQUIRED)
Defensive patterns

Strategy: validation

Validate before calling

import ssl
VALID_CERT_REQS = {"none", "optional", "required"}
def normalize_cert_reqs(value):
    if isinstance(value, str):
        if value not in VALID_CERT_REQS:
            raise ValueError(f"Use one of {VALID_CERT_REQS} or an ssl.CERT_* constant")
    return value

Type guard

def is_valid_cert_reqs(value) -> bool:
    import ssl
    if isinstance(value, ssl.VerifyMode):
        return True
    return value in ("none", "optional", "required")

Try / catch

from redis.exceptions import RedisError
try:
    r = redis.asyncio.Redis(..., ssl=True, ssl_cert_reqs=req)
except RedisError as e:
    if "Invalid SSL Certificate" in str(e):
        r = redis.asyncio.Redis(..., ssl=True, ssl_cert_reqs="required")
    else:
        raise

Prevention

When it happens

Trigger: Passing ssl_cert_reqs='CERT_REQUIRED', ssl_cert_reqs='require', or any non-canonical string (case-sensitive, must be lowercase 'none'/'optional'/'required') to SSLConnection or via Redis(...)/from_url with ssl params. The lookup table at line 1635 only contains lowercase keys.

Common situations: Copying the ssl module's constant NAMES ('CERT_REQUIRED') instead of the short forms; typos like 'req'/'optional2'; mixing up with the Redis URL query param ssl_cert_reqs which forwards the string here.

Related errors


AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04). Data as JSON: /data/errors/9691f4d600d4ba86.json. Report an issue: GitHub.