{"id":"91300caf0de6c1cf","repo":"redis/redis-py","slug":"hashed-password-i-must-be-prefixed-with-a-to","errorCode":null,"errorMessage":"Hashed password {i} must be prefixed with a \"+\" to add or a \"-\" to remove","messagePattern":"Hashed password (.+?) must be prefixed with a \"\\+\" to add or a \"-\" to remove","errorType":"validation","errorClass":"DataError","httpStatus":null,"severity":"error","filePath":"redis/commands/core.py","lineNumber":513,"sourceCode":"                    pieces.append(b\"<%s\" % password[1:])\n                else:\n                    raise DataError(\n                        f\"Password {i} must be prefixed with a \"\n                        f'\"+\" to add or a \"-\" to remove'\n                    )\n\n        if hashed_passwords:\n            # as most users will have only one password, allow remove_passwords\n            # to be specified as a simple string or a list\n            hashed_passwords = list_or_args(hashed_passwords, [])\n            for i, hashed_password in enumerate(hashed_passwords):\n                hashed_password = encoder.encode(hashed_password)\n                if hashed_password.startswith(b\"+\"):\n                    pieces.append(b\"#%s\" % hashed_password[1:])\n                elif hashed_password.startswith(b\"-\"):\n                    pieces.append(b\"!%s\" % hashed_password[1:])\n                else:\n                    raise DataError(\n                        f\"Hashed password {i} must be prefixed with a \"\n                        f'\"+\" to add or a \"-\" to remove'\n                    )\n\n        if nopass:\n            pieces.append(b\"nopass\")\n\n        if categories:\n            for category in categories:\n                category = encoder.encode(category)\n                # 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\"-\"):","sourceCodeStart":495,"sourceCodeEnd":531,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/commands/core.py#L495-L531","documentation":"Raised by acl_setuser() as a DataError when an entry in hashed_passwords does not start with '+' (add) or '-' (remove). Hashed passwords are SHA-256 hex strings and, like plain passwords, must be prefixed so the client can encode them as #hash (add) or !hash (remove). The offending entry's index i is in the message. See redis/commands/core.py:506-516.","triggerScenarios":"Calling r.acl_setuser(username, hashed_passwords=['5e88...']) (no prefix), or a list where any hash lacks +/- . Single prefixed string accepted via list_or_args.","commonSituations":"Pre-hashing passwords server-side or client-side and forgetting the prefix; integrating with an identity store that stores bare hashes; porting from a tool that does not require prefixes.","solutions":["Prefix each hash with '+' to add or '-' to remove, e.g. ['+5e884...'].","Normalize on add: hashed_passwords = ['+' + h for h in raw_hashes].","Confirm hashes are hex-encoded SHA-256; non-hex content will be rejected by the server separately."],"exampleFix":"# before\nr.acl_setuser('alice', enabled=True, hashed_passwords=['5e884898da28'])\n# after\nr.acl_setuser('alice', enabled=True, hashed_passwords=['+5e884898da28'])","handlingStrategy":"validation","validationCode":"def _prefixed_hashes(items):\n    for i, h in enumerate(items):\n        if h[:1] not in ('+', '-'):\n            raise ValueError(f'hashed password {i} needs +/- prefix')\n    return items\nclient.acl_setuser(username, hashed_passwords=_prefixed_hashes(hashed_passwords or []))","typeGuard":"def hashed_passwords_are_prefixed(hashed_passwords) -> bool:\n    return all(h[:1] in ('+', '-') for h in (hashed_passwords or []))","tryCatchPattern":"from redis.exceptions import DataError\ntry:\n    client.acl_setuser(username, hashed_passwords=hashed_passwords)\nexcept DataError:\n    hashed_passwords = ['+' + h for h in hashed_passwords]\n    client.acl_setuser(username, hashed_passwords=hashed_passwords)","preventionTips":["Prefix hashes when you generate or import them.","Keep the +/- sign in your identity store, not as a default."],"tags":["acl","validation","setuser","hashed-password","prefix"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}