redis/redis-py · error · DataError
Command "{cmd}" must be prefixed with "+" or "-"
Error message
Command "{cmd}" must be prefixed with "+" or "-" What it means
Raised by acl_setuser() as a DataError when an entry in the commands iterable does not start with '+' or '-'. Each command permission must explicitly grant (+) or revoke (-) a command, optionally with args (e.g. '+get', '-flushdb'). The decoded command is shown. See redis/commands/core.py:538-546.
Source
Thrown at redis/commands/core.py:542
# 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"-"):
pieces.append(b"-@%s" % category[1:])
else:
raise DataError(
f'Category "{encoder.decode(category, force=True)}" '
'must be prefixed with "+" or "-"'
)
if commands:
for cmd in commands:
cmd = encoder.encode(cmd)
if not cmd.startswith(b"+") and not cmd.startswith(b"-"):
raise DataError(
f'Command "{encoder.decode(cmd, force=True)}" '
'must be prefixed with "+" or "-"'
)
pieces.append(cmd)
if keys:
for key in keys:
key = encoder.encode(key)
if not key.startswith(b"%") and not key.startswith(b"~"):
key = b"~%s" % key
pieces.append(key)
if channels:
for channel in channels:
channel = encoder.encode(channel)
pieces.append(b"&%s" % channel)
if selectors:View on GitHub (pinned to da03cdc7e8)
Solutions
- Prefix each command with '+' (allow) or '-' (deny), e.g. ['+get', '+set', '-flushdb'].
- Normalize from a (cmd, allow) tuple: commands = [('+' if a else '-') + c for c, a in rules].
- Keep the sign paired with the command in your permission store, not as a separate default.
Example fix
# before
r.acl_setuser('alice', enabled=True, commands=['get', 'set'])
# after
r.acl_setuser('alice', enabled=True, commands=['+get', '+set']) Defensive patterns
Strategy: validation
Validate before calling
def _prefixed_commands(items):
for cmd in items:
if cmd[:1] not in ('+', '-'):
raise ValueError(f'command {cmd!r} needs +/- prefix')
return items
client.acl_setuser(username, commands=_prefixed_commands(commands or [])) Type guard
def commands_are_prefixed(commands) -> bool:
return all(cmd[:1] in ('+', '-') for cmd in (commands or [])) Try / catch
from redis.exceptions import DataError
try:
client.acl_setuser(username, commands=commands)
except DataError:
commands = ['+' + c for c in commands]
client.acl_setuser(username, commands=commands) Prevention
- Store commands as (name, allow) pairs, then render the sign.
- Never pass raw command names from a user-facing list.
When it happens
Trigger: Calling r.acl_setuser(username, commands=['get', 'set']) (no prefix), or any command string missing the leading sign.
Common situations: Listing allowed commands as bare names in config; building a permission editor that treats commands as toggleable names without storing the grant/revoke sign.
Related errors
- Password {i} must be prefixed with a "+" to add or a "-" to
- Hashed password {i} must be prefixed with a "+" to add or a
- Category "{category}" 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/e2cdd717d6669daf.json.
Report an issue: GitHub.