redis/redis-py · error · DataError

Hashed password must be prefixed with a "+" to add or a "-"…

Error message

Hashed password {i} must be prefixed with a "+" to add or a "-" to remove

What it means

Raised by Redis.acl_setuser() when an entry in the `hashed_passwords` list does not begin with '+' (add) or '-' (remove). Like plain passwords, each SHA-256 hashed password must be prefixed to indicate add vs remove. The library inspects the first byte of the encoded entry and rejects missing prefixes.

Solutions

  1. Prefix every hashed_passwords entry with '+' to add or '-' to remove.
  2. Generate the hash correctly: hashlib.sha256(password.encode()).hexdigest(), then prepend '+' or '-'.
  3. If migrating from plain passwords, switch to the `passwords` argument instead and let Redis hash them server-side.

Example fix

# before
import hashlib
h = hashlib.sha256(b'secret').hexdigest()
client.acl_setuser('alice', hashed_passwords=[h])
# after
client.acl_setuser('alice', hashed_passwords=['+' + h])
Defensive patterns

Strategy: validation

Validate before calling

import hashlib
def make_hashed_password(cleartext: str, remove: bool = False) -> str:
    h = hashlib.sha256(cleartext.encode()).hexdigest()
    return ('-' if remove else '+') + h

def normalize_hashed(items):
    out = []
    for h in list_or_args(items, []):
        if not (h.startswith('+') or h.startswith('-')):
            h = '+' + h
        out.append(h)
    return out

Type guard

def is_prefixed_hashed(h) -> bool:
    return isinstance(h, str) and len(h) > 1 and h[0] in '+-'

Try / catch

from redis.exceptions import DataError
try:
    client.acl_setuser('alice', hashed_passwords=hashed)
except DataError as e:
    if 'must be prefixed' in str(e):
        hashed = ['+' + h if not h[:1] in '+-' else h for h in hashed]
        client.acl_setuser('alice', hashed_passwords=hashed)
    else:
        raise

Prevention

When it happens

Trigger: Calling client.acl_setuser('alice', hashed_passwords=['5e88...']) with no prefix, or with a wrong prefix. Hashed passwords are SHA-256 hex strings of the cleartext password and must still carry '+'/'-' on the client side.

Common situations: Precomputing SHA-256 hashes and forgetting the prefix; porting hashes from redis-cli ACL SETUSER syntax that uses '#'/'!' separators (those are added internally by the client based on '+'/'-').

Related errors


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

Appendix: source

Thrown at redis/commands/core.py:513

                    pieces.append(b"<%s" % password[1:])
                else:
                    raise DataError(
                        f"Password {i} must be prefixed with a "
                        f'"+" to add or a "-" to remove'
                    )

        if hashed_passwords:
            # as most users will have only one password, allow remove_passwords
            # to be specified as a simple string or a list
            hashed_passwords = list_or_args(hashed_passwords, [])
            for i, hashed_password in enumerate(hashed_passwords):
                hashed_password = encoder.encode(hashed_password)
                if hashed_password.startswith(b"+"):
                    pieces.append(b"#%s" % hashed_password[1:])
                elif hashed_password.startswith(b"-"):
                    pieces.append(b"!%s" % hashed_password[1:])
                else:
                    raise DataError(
                        f"Hashed password {i} must be prefixed with a "
                        f'"+" to add or a "-" to remove'
                    )

        if nopass:
            pieces.append(b"nopass")

        if categories:
            for category in categories:
                category = encoder.encode(category)
                # categories can be prefixed with one of (+@, +, -@, -)
                if category.startswith(b"+@"):
                    pieces.append(category)
                elif category.startswith(b"+"):
                    pieces.append(b"+@%s" % category[1:])
                elif category.startswith(b"-@"):
                    pieces.append(category)
                elif category.startswith(b"-"):

View on GitHub (pinned to 6a6b581b48)