redis/redis-py · error · DataError
Command "{encoder.decode(cmd, force=True)}" must be prefixed
Error message
Command "{encoder.decode(cmd, force=True)}" must be prefixed with "+" or "-" What it means
Raised by Redis.acl_setuser() when an entry in the `commands` list does not begin with '+' or '-'. Each command permission must be prefixed to indicate grant or revoke (e.g. '+get', '-flushdb'). The library inspects the first byte of the encoded command and rejects entries missing the prefix; the decoded command name is shown in the message.
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 6a6b581b48)
Solutions
- Prefix every commands entry with '+' (grant) or '-' (revoke), e.g. '+get', '-flushdb'.
- For grouping, use `categories` with '+@name' instead of listing many commands.
- Validate prefixes upstream in your permissions builder.
Example fix
# before
client.acl_setuser('alice', commands=['get', 'set'])
# after
client.acl_setuser('alice', commands=['+get', '+set']) Defensive patterns
Strategy: validation
Validate before calling
def normalize_commands(commands):
out = []
for c in commands:
if not (c.startswith('+') or c.startswith('-')):
c = '+' + c # default to grant
out.append(c)
return out
def safe_acl_setuser_commands(client, username, commands):
return client.acl_setuser(username, commands=normalize_commands(commands)) Type guard
def is_prefixed_command(c) -> bool:
return isinstance(c, str) and len(c) > 1 and c[0] in '+-' Try / catch
from redis.exceptions import DataError
try:
client.acl_setuser('alice', commands=commands)
except DataError as e:
if 'must be prefixed' in str(e):
commands = ['+' + c if not c[:1] in '+-' else c for c in commands]
client.acl_setuser('alice', commands=commands)
else:
raise Prevention
- Always prefix command permissions with '+'/'-'.
- For grouped permissions, prefer `categories` ('+@name') over long command lists.
- Validate ACL inputs in a shared helper reused across callsites.
When it happens
Trigger: Calling client.acl_setuser('alice', commands=['get']) with no prefix, commands=['get', '+set'] (mixed), or commands=['@get']. The decoded offending command appears in the message.
Common situations: Passing raw command names from a permissions UI without prefix; confusing command permissions with category permissions (which use '@'); copy-pasting a command list from docs that omit prefixes for brevity.
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 "{encoder.decode(category, force=True)}" must be pr
- 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@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/911f4f1439b41140.
Report an issue: GitHub.