redis/redis-py · error · DataError
Category "{encoder.decode(category, force=True)}" must be pr
Error message
Category "{encoder.decode(category, force=True)}" must be prefixed with "+" or "-" What it means
Raised by Redis.acl_setuser() when an entry in the `categories` list does not begin with '+' or '-'. Each ACL category permission must be prefixed to indicate grant or revoke. The library accepts forms like '+@category', '-@category', '+category' (rewritten to '+@category'), or '-category'; any entry lacking a leading '+'/'-' is rejected. The decoded (human-readable) category value is interpolated into the message.
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 6a6b581b48)
Solutions
- Prefix every categories entry with '+' (grant) or '-' (revoke), e.g. '+@read' or '-@dangerous'.
- Use the shorthand '+read' which the client rewrites to '+@read' internally.
- Validate prefixes in your config layer before building the call.
Example fix
# before
client.acl_setuser('alice', categories=['@read', '@write'])
# after
client.acl_setuser('alice', categories=['+@read', '+@write']) Defensive patterns
Strategy: validation
Validate before calling
def normalize_categories(categories):
out = []
for c in categories:
if not (c.startswith('+') or c.startswith('-')):
c = '+' + c # default to grant
out.append(c)
return out
def safe_acl_setuser_categories(client, username, categories):
return client.acl_setuser(username, categories=normalize_categories(categories)) Type guard
def is_prefixed_category(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', categories=categories)
except DataError as e:
if 'must be prefixed' in str(e):
categories = ['+' + c if not c[:1] in '+-' else c for c in categories]
client.acl_setuser('alice', categories=categories)
else:
raise Prevention
- Use '+@name' / '-@name' consistently for category permissions.
- Wrap acl_setuser in a helper that enforces prefixes for categories, commands, and selectors.
- Validate ACL config files at load time before applying.
When it happens
Trigger: Calling client.acl_setuser('alice', categories=['@read']) (leading '@' without '+'/'-'), categories=['read', '+write'] (some unprefixed), or categories=['=read']. The error message echoes the offending category.
Common situations: Assuming the client wraps bare category names with '+@'; passing the redis-cli form '@read' verbatim; mixing prefixed and unprefixed entries.
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 "{encoder.decode(cmd, force=True)}" must be prefixed
- 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/bda48e8dc4081ae3.
Report an issue: GitHub.