redis/redis-py · error · DataError
Hashed password {i} 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 acl_setuser() as a DataError when an entry in hashed_passwords does not start with '+' (add) or '-' (remove). Hashed passwords are SHA-256 hex strings and, like plain passwords, must be prefixed so the client can encode them as #hash (add) or !hash (remove). The offending entry's index i is in the message. See redis/commands/core.py:506-516.
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 da03cdc7e8)
Solutions
- Prefix each hash with '+' to add or '-' to remove, e.g. ['+5e884...'].
- Normalize on add: hashed_passwords = ['+' + h for h in raw_hashes].
- Confirm hashes are hex-encoded SHA-256; non-hex content will be rejected by the server separately.
Example fix
# before
r.acl_setuser('alice', enabled=True, hashed_passwords=['5e884898da28'])
# after
r.acl_setuser('alice', enabled=True, hashed_passwords=['+5e884898da28']) Defensive patterns
Strategy: validation
Validate before calling
def _prefixed_hashes(items):
for i, h in enumerate(items):
if h[:1] not in ('+', '-'):
raise ValueError(f'hashed password {i} needs +/- prefix')
return items
client.acl_setuser(username, hashed_passwords=_prefixed_hashes(hashed_passwords or [])) Type guard
def hashed_passwords_are_prefixed(hashed_passwords) -> bool:
return all(h[:1] in ('+', '-') for h in (hashed_passwords or [])) Try / catch
from redis.exceptions import DataError
try:
client.acl_setuser(username, hashed_passwords=hashed_passwords)
except DataError:
hashed_passwords = ['+' + h for h in hashed_passwords]
client.acl_setuser(username, hashed_passwords=hashed_passwords) Prevention
- Prefix hashes when you generate or import them.
- Keep the +/- sign in your identity store, not as a default.
When it happens
Trigger: Calling r.acl_setuser(username, hashed_passwords=['5e88...']) (no prefix), or a list where any hash lacks +/- . Single prefixed string accepted via list_or_args.
Common situations: Pre-hashing passwords server-side or client-side and forgetting the prefix; integrating with an identity store that stores bare hashes; porting from a tool that does not require prefixes.
Related errors
- Password {i} must be prefixed with a "+" to add or a "-" to
- Category "{category}" must be prefixed with "+" or "-"
- Command "{cmd}" must be prefixed with "+" or "-"
- Cannot set 'nopass' and supply 'passwords' or 'hashed_passwo
- genpass optionally accepts a bits argument, between 0 and 40
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/91300caf0de6c1cf.json.
Report an issue: GitHub.