{"id":"e2cdd717d6669daf","repo":"redis/redis-py","slug":"command-cmd-must-be-prefixed-with-or","errorCode":null,"errorMessage":"Command \"{cmd}\" must be prefixed with \"+\" or \"-\"","messagePattern":"Command \"(.+?)\" must be prefixed with \"\\+\" or \"-\"","errorType":"validation","errorClass":"DataError","httpStatus":null,"severity":"error","filePath":"redis/commands/core.py","lineNumber":542,"sourceCode":"                # categories can be prefixed with one of (+@, +, -@, -)\n                if category.startswith(b\"+@\"):\n                    pieces.append(category)\n                elif category.startswith(b\"+\"):\n                    pieces.append(b\"+@%s\" % category[1:])\n                elif category.startswith(b\"-@\"):\n                    pieces.append(category)\n                elif category.startswith(b\"-\"):\n                    pieces.append(b\"-@%s\" % category[1:])\n                else:\n                    raise DataError(\n                        f'Category \"{encoder.decode(category, force=True)}\" '\n                        'must be prefixed with \"+\" or \"-\"'\n                    )\n        if commands:\n            for cmd in commands:\n                cmd = encoder.encode(cmd)\n                if not cmd.startswith(b\"+\") and not cmd.startswith(b\"-\"):\n                    raise DataError(\n                        f'Command \"{encoder.decode(cmd, force=True)}\" '\n                        'must be prefixed with \"+\" or \"-\"'\n                    )\n                pieces.append(cmd)\n\n        if keys:\n            for key in keys:\n                key = encoder.encode(key)\n                if not key.startswith(b\"%\") and not key.startswith(b\"~\"):\n                    key = b\"~%s\" % key\n                pieces.append(key)\n\n        if channels:\n            for channel in channels:\n                channel = encoder.encode(channel)\n                pieces.append(b\"&%s\" % channel)\n\n        if selectors:","sourceCodeStart":524,"sourceCodeEnd":560,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/commands/core.py#L524-L560","documentation":"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.","triggerScenarios":"Calling r.acl_setuser(username, commands=['get', 'set']) (no prefix), or any command string missing the leading sign.","commonSituations":"Listing allowed commands as bare names in config; building a permission editor that treats commands as toggleable names without storing the grant/revoke sign.","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."],"exampleFix":"# before\nr.acl_setuser('alice', enabled=True, commands=['get', 'set'])\n# after\nr.acl_setuser('alice', enabled=True, commands=['+get', '+set'])","handlingStrategy":"validation","validationCode":"def _prefixed_commands(items):\n    for cmd in items:\n        if cmd[:1] not in ('+', '-'):\n            raise ValueError(f'command {cmd!r} needs +/- prefix')\n    return items\nclient.acl_setuser(username, commands=_prefixed_commands(commands or []))","typeGuard":"def commands_are_prefixed(commands) -> bool:\n    return all(cmd[:1] in ('+', '-') for cmd in (commands or []))","tryCatchPattern":"from redis.exceptions import DataError\ntry:\n    client.acl_setuser(username, commands=commands)\nexcept DataError:\n    commands = ['+' + c for c in commands]\n    client.acl_setuser(username, commands=commands)","preventionTips":["Store commands as (name, allow) pairs, then render the sign.","Never pass raw command names from a user-facing list."],"tags":["acl","validation","setuser","command","prefix"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}