{"record":{"id":"c89f114100530186","repo":"microsoft/garnet","slug":"op","errorCode":null,"errorMessage":"{op}","messagePattern":"\\{op\\}","errorType":"exception","errorClass":"ACLUnknownOperationException","httpStatus":null,"severity":"error","filePath":"libs/server/ACL/ACLParser.cs","lineNumber":270,"sourceCode":"                else\n                {\n                    throw new AclCommandDoesNotExistException(commandName);\n                }\n            }\n            else if (op.Equals(\"~*\", StringComparison.Ordinal) || op.Equals(\"ALLKEYS\", StringComparison.OrdinalIgnoreCase))\n            {\n                // NOTE: No-op, because only wildcard key patterns are currently supported. If per-key key\n                // patterns are ever added, the GET scatter-gather fast path (NetworkGET_SG) must re-check\n                // ACL per key: it serves GETs past the first without returning through the per-command ACL\n                // check in ProcessMessages, which is only safe while key access is all-or-nothing.\n            }\n            else if (op.Equals(\"RESETKEYS\", StringComparison.OrdinalIgnoreCase))\n            {\n                // NOTE: No-op, because only wildcard key patterns are currently supported\n            }\n            else\n            {\n                throw new ACLUnknownOperationException(op);\n            }\n\n            // There's some fixup that has to be done when parsing a command\n            static bool TryParseCommandForAcl(string commandName, out RespCommand command)\n            {\n                int subCommandSepIx = commandName.IndexOf('|');\n                bool isSubCommand = subCommandSepIx != -1;\n\n                string effectiveName = isSubCommand ? commandName[..subCommandSepIx] + \"_\" + commandName[(subCommandSepIx + 1)..] : commandName;\n\n                if (!Enum.TryParse(effectiveName, ignoreCase: true, out command) || !IsValidParse(command, effectiveName))\n                {\n                    // Try replacing dots with empty strings for commands like RI.CREATE -> RICREATE\n                    string dotlessName = effectiveName.Replace(\".\", \"\");\n                    if (dotlessName != effectiveName && Enum.TryParse(dotlessName, ignoreCase: true, out command) && IsValidParse(command, dotlessName))\n                    {\n                        // Successfully parsed after removing dots — fall through to validation below\n                    }","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/server/ACL/ACLParser.cs#L252-L288","documentation":"Thrown by ACLParser.ApplyACLOpToUser when an operation token matches none of the recognized forms (ON/OFF/NOPASS/RESET/RESETPASS, >/</#/!, +/-@category, +/-command, ~*/ALLKEYS, RESETKEYS). Any other token is reported verbatim via ACLUnknownOperationException. This is the catch-all for completely unrecognized ACL operations.","triggerScenarios":"A typo such as 'oNn' (close to but not 'on'), a stray token like 'allcommands' (Garnet does not support that; categories are used instead), or a value without an operator prefix (e.g. a bare password 'secret' instead of '>secret').","commonSituations":"Copy-pasting Redis ACL syntax that Garnet does not implement (e.g. 'allchannels', 'resetchannels', '>&' no-password rules); a delimiter issue leaving a fragment as its own token; hand-editing that introduced a typo.","solutions":["Check the token against the supported operation set in ApplyACLOpToUser.","Prefix passwords with '>'/'<' and hashes with '#'/!' rather than leaving them bare.","Use categories (+@all / -@dangerous) instead of unsupported 'allcommands'.","Remove any operation Garnet does not support rather than leaving an unrecognized token."],"exampleFix":"// before\nACLParser.ParseACLRule(\"user alice on allcommands secret\");\n\n// after\nACLParser.ParseACLRule(\"user alice on +@all >secret\");","handlingStrategy":"validation","validationCode":"var supported = new HashSet<string>(StringComparer.OrdinalIgnoreCase){\"on\",\"off\",\"nopass\",\"reset\",\"resetpass\",\"allkeys\",\"resetkeys\"};\n// plus prefixed forms >, <, #, !, +, -, +@, -@, ~*\nif (!(supported.Contains(op) || \">/</#/!/+/-(+@/-@/~*)\".Any(p => op.StartsWith(p))))\n    throw new ArgumentException($\"Unsupported ACL operation: {op}\");","typeGuard":"static bool IsSupportedOp(string op) =>\n    op.Length>0 && (op[0]=='>'||op[0]=='<'||op[0]=='#'||op[0]=='!'||op[0]=='+'||op[0]=='-'||\n    op.Equals(\"on\",StringComparison.OrdinalIgnoreCase)||op.Equals(\"off\",StringComparison.OrdinalIgnoreCase)||\n    op.Equals(\"allkeys\",StringComparison.OrdinalIgnoreCase)||op.Equals(\"resetkeys\",StringComparison.OrdinalIgnoreCase)||\n    op.Equals(\"nopass\",StringComparison.OrdinalIgnoreCase)||op.Equals(\"reset\",StringComparison.OrdinalIgnoreCase)||\n    op.Equals(\"resetpass\",StringComparison.OrdinalIgnoreCase));","tryCatchPattern":"try { ACLParser.ParseACLRule(line, acl); }\ncatch (ACLUnknownOperationException ex) { logger.LogError(\"Unknown op: {Op}\", ex.Message); }","preventionTips":["Prefix passwords with '>' and hashes with '#'.","Use +@/-@ categories instead of unsupported 'allcommands'.","Drop operations Garnet does not implement rather than leaving stray tokens."],"tags":["acl","parsing","operation","configuration","validation"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}