{"record":{"id":"df238f4a7628962a","repo":"microsoft/garnet","slug":"unknown-custom-command-name-not-registered-wi","errorCode":null,"errorMessage":"Unknown custom command '{name}' (not registered with any loaded module)","messagePattern":"Unknown custom command '(.+?)' \\(not registered with any loaded module\\)","errorType":"exception","errorClass":"ACLException","httpStatus":null,"severity":"error","filePath":"libs/server/Resp/ACLCommands.cs","lineNumber":214,"sourceCode":"                    for (var i = 1; i < ops.Length; i++)\n                    {\n                        ACLParser.ApplyACLOpToUser(ref newUser, ops[i]);\n                    }\n\n                    // At SETUSER time modules are loaded; any newly-added per-name custom permission\n                    // must resolve against CustomCommandManager. Unknown names here are almost always\n                    // typos - fail closed.\n                    var ccm = storeWrapper.customCommandManager;\n                    if (ccm != null)\n                    {\n                        foreach (var name in newUser.CustomCommandsAllowed)\n                        {\n                            // A name already present in either pre-op set (allow/deny) is not \"new\" from\n                            // the system's POV - only flag names absent from both presets and unregistered.\n                            // This permits toggling (allow↔deny) of loose-loaded names without false-positive throws.\n                            if (!preAllowed.Contains(name) && !preDenied.Contains(name) && !ccm.IsCustomCommandRegistered(name))\n                            {\n                                throw new ACLException($\"Unknown custom command '{name}' (not registered with any loaded module)\");\n                            }\n                        }\n                        foreach (var name in newUser.CustomCommandsDenied)\n                        {\n                            if (!preAllowed.Contains(name) && !preDenied.Contains(name) && !ccm.IsCustomCommandRegistered(name))\n                            {\n                                throw new ACLException($\"Unknown custom command '{name}' (not registered with any loaded module)\");\n                            }\n                        }\n                    }\n                }\n                while (!userHandle.TrySetUser(newUser, currentUser));\n            }\n            catch (ACLException exception)\n            {\n                // Abort command execution\n                while (!RespWriteUtils.TryWriteError($\"ERR {exception.Message}\", ref dcurr, dend))\n                    SendAndReset();","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/server/Resp/ACLCommands.cs#L196-L232","documentation":"Thrown during ACL SETUSER (the retry-loop that applies ACL ops) when a newly-added entry in the user's CustomCommandsAllowed set does not match any registered custom command. The check is intentionally limited to names not already present in the pre-op allow/deny sets (so toggling legacy names does not false-positive), so it fires specifically for freshly-introduced, unregistered names - almost always a typo or a missing module.","triggerScenarios":"An ACL SETUSER rule adds a +<customname> allow entry where <customname> is not registered with CustomCommandManager and was not already in the user's allow/deny sets.","commonSituations":"Typing a custom command name wrong in an ACL rule; referencing a custom command from a module that is not loaded; applying an ACL file before the module defining the command has loaded (for names not already preset).","solutions":["Correct the command name in the ACL rule to match the registered custom command exactly (case-sensitive).","Ensure the module that registers the custom command is loaded before applying the ACL rule.","List registered custom commands (e.g., via COMMAND DOCS / module introspection) to confirm the exact name."],"exampleFix":"# before\nACL SETUSER alice on >pass +mycuston   # typo: should be mycustom\n# after\nACL SETUSER alice on >pass +mycustom","handlingStrategy":"validation","validationCode":"// Before issuing ACL SETUSER with custom command rules\nforeach (var name in allowCustomNames) {\n    if (!storeWrapper.customCommandManager.IsCustomCommandRegistered(name))\n        throw new InvalidOperationException($\"Refusing ACL rule: custom command '{name}' is not registered.\");\n}","typeGuard":null,"tryCatchPattern":"catch (ACLException ex) when (ex.Message.Contains(\"Unknown custom command\")) {\n    // ACL rule referenced an unregistered name; surface to operator, do not persist the user change\n    logger.LogWarning(\"ACL SETUSER rejected: {Msg}\", ex.Message);\n}","preventionTips":["Load all modules before loading ACL files.","Validate custom command names against the registered set before applying ACL rules.","Note only newly-added names are checked; pre-existing names are grandfathered."],"tags":["acl","custom-commands","config","module"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}