redis/redis-py · error · DataError
Category "{category}" must be prefixed with "+" or "-"
Error message
Category "{category}" must be prefixed with "+" or "-" What it means
Raised by acl_setuser() as a DataError when an entry in categories does not start with '+' or '-' (the '@category' suffix is optional and auto-added by the client). Categories grant/deny command categories like @read, @write, @dangerous. The decoded category value is shown. See redis/commands/core.py:521-537.
Source
Thrown at redis/commands/core.py:534
)
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"-"):
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" % keyView on GitHub (pinned to da03cdc7e8)
Solutions
- Prefix each category with '+' (grant) or '-' (revoke), e.g. ['+read', '-dangerous']. The '@' is optional.
- Normalize: categories = [('+' if grant else '-') + c for c in raw].
- Drive the sign from a permission model in your config rather than letting callers pass raw strings.
Example fix
# before
r.acl_setuser('alice', enabled=True, categories=['read', 'write'])
# after
r.acl_setuser('alice', enabled=True, categories=['+read', '+write']) Defensive patterns
Strategy: validation
Validate before calling
def _prefixed_categories(items):
for c in items:
if c[:1] not in ('+', '-'):
raise ValueError(f'category {c!r} needs +/- prefix')
return items
client.acl_setuser(username, categories=_prefixed_categories(categories or [])) Type guard
def categories_are_prefixed(categories) -> bool:
return all(c[:1] in ('+', '-') for c in (categories or [])) Try / catch
from redis.exceptions import DataError
try:
client.acl_setuser(username, categories=categories)
except DataError:
categories = ['+' + c.lstrip('+-@') for c in categories]
client.acl_setuser(username, categories=categories) Prevention
- Always pair a category name with a grant/revoke sign in config.
- The '@' is optional; the sign is not.
When it happens
Trigger: Calling r.acl_setuser(username, categories=['read', 'write']) (no prefix), or a list where any entry lacks +/- . '+@read' and '-@write' are also accepted as-is.
Common situations: Reading category names from config without the permission sign; UI listing categories as bare names; assuming the client defaults to '+' for grant.
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
- 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/b731b7b0ff23e080.json.
Report an issue: GitHub.