{"record":{"id":"886c0fb024aeaebe","repo":"microsoft/garnet","slug":"acl-strict-mode-unresolved-count-unresolved-us","errorCode":null,"errorMessage":"ACL strict mode: {unresolved.Count} unresolved (user, custom-command) entries in ACL rules: {entries}. Disable acl-strict-custom-commands or load the appropriate module(s).","messagePattern":"ACL strict mode: (.+?) unresolved \\(user, custom-command\\) entries in ACL rules: (.+?)\\. Disable acl-strict-custom-commands or load the appropriate module\\(s\\)\\.","errorType":"exception","errorClass":"GarnetException","httpStatus":null,"severity":"error","filePath":"libs/host/GarnetServer.cs","lineNumber":360,"sourceCode":"                }\n            }\n\n            if (unresolved.Count == 0)\n            {\n                return;\n            }\n\n            foreach (var (user, name) in unresolved)\n            {\n                logger?.LogWarning(\"ACL rule references custom command '{name}' for user '{user}' which is not registered with any loaded module\", name, user);\n            }\n\n            if (opts.AclStrictCustomCommands)\n            {\n                // Strict mode: fail closed so operators can't accidentally ship an ACL with typos\n                // that would silently match no command (and therefore deny by default at dispatch).\n                var entries = string.Join(\", \", unresolved.Select(t => $\"({t.user},{t.name})\"));\n                throw new GarnetException($\"ACL strict mode: {unresolved.Count} unresolved (user, custom-command) entries in ACL rules: {entries}. Disable acl-strict-custom-commands or load the appropriate module(s).\");\n            }\n        }\n\n        private GarnetDatabase CreateDatabase(int dbId, GarnetServerOptions serverOptions, ClusterFactory clusterFactory,\n            CustomCommandManager customCommandManager)\n        {\n            var removeOutdated = !serverOptions.EnableCluster;\n            // Two-roots layout for RangeIndex files:\n            //  riLogRoot — log-tied (working file + per-flush snapshots), co-located with hlog.\n            //              Falls back through LogDir → CheckpointDir → cwd, mirroring Tsavorite's\n            //              CheckpointBaseDirectory chain so RangeIndex works without storage tier.\n            //  cprDir    — checkpoint-tied (per-token snapshots live under <token>/rangeindex/),\n            //              alongside Tsavorite's cpr-checkpoints/<token>/info.dat etc.\n            // Construct the manager only when the feature is enabled. When disabled, the\n            // store wrapper / triggers / functions hold a null reference, and Tsavorite's\n            // record-trigger gates (CallOnFlush etc.) return false → zero per-op overhead.\n            RangeIndexManager rangeIndexManager = null;\n            if (serverOptions.EnableRangeIndexPreview)","sourceCodeStart":342,"sourceCodeEnd":378,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/host/GarnetServer.cs#L342-L378","documentation":"Thrown during GarnetServer initialization when ACL strict mode (opts.AclStrictCustomCommands) is enabled and one or more ACL rules reference custom commands that are not registered with any loaded module. The strict mode is a fail-closed safety mechanism: it prevents operators from shipping ACL configurations with typos that would silently deny commands. The error lists the specific unresolved user-command pairs.","triggerScenarios":"An ACL configuration grants or denies a custom command (via CustomCommandsAllowed or CustomCommandsDenied) for a user, but no loaded Garnet module has registered that command name. With AclStrictCustomCommands=true, this is treated as a fatal startup error.","commonSituations":"Deploying an ACL file that references module commands before loading the module; typos in custom command names in the ACL file; upgrading a module that renamed or removed a command without updating the ACL; developing custom modules where the command registration name differs from the ACL rule.","solutions":["Load the appropriate module(s) that register the referenced custom commands before the ACL is validated.","Fix typos in the ACL file — the error message lists the specific (user, command) pairs.","If you intentionally want to ship ACLs with not-yet-loaded commands, set AclStrictCustomCommands=false (but note this weakens the safety check).","Verify that module command registration names exactly match the names used in ACL rules."],"exampleFix":"// before: ACL references 'MYCMD' but module not loaded\n//   user alice on >password ~* +MYCMD\n\n// after: load module first or fix command name\n//   garnet-server --loadmodule ./mymodule.so\n//   user alice on >password ~* +MYCMD","handlingStrategy":"validation","validationCode":"// Before strict validation, verify all ACL-referenced custom commands are registered\nforeach (var user in aclUsers)\n{\n    foreach (var cmd in user.CustomCommandsAllowed.Concat(user.CustomCommandsDenied))\n    {\n        if (!customCommandManager.IsCustomCommandRegistered(cmd))\n            throw new InvalidOperationException(\n                $\"ACL references unregistered custom command '{cmd}' for user '{user.Name}'. Load the module first.\");\n    }\n}","typeGuard":null,"tryCatchPattern":"try\n{\n    server = new GarnetServer(commandLineArgs, loggerFactory);\n}\ncatch (GarnetException ex) when (ex.Message.Contains(\"ACL strict mode\"))\n{\n    logger.LogError(ex, \"ACL references unresolved custom commands. Load modules or disable acl-strict-custom-commands.\");\n    throw;\n}","preventionTips":["Load all modules before the ACL is validated at startup.","Ensure ACL command names exactly match module registration names.","Set AclStrictCustomCommands=false in development when iterating on modules (re-enable for production).","Audit ACL files against loaded modules during CI."],"tags":["acl","security","custom-commands","modules","startup"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}