{"record":{"id":"2a3411aea0f85628","repo":"microsoft/garnet","slug":"customtransaction-name-is-null","errorCode":null,"errorMessage":"CustomTransaction name is null","messagePattern":"CustomTransaction name is null","errorType":"validation","errorClass":"GarnetException","httpStatus":null,"severity":"error","filePath":"libs/server/Custom/CustomTransaction.cs","lineNumber":21,"sourceCode":"\nusing System;\nusing Garnet.common;\n\nnamespace Garnet.server\n{\n    class CustomTransaction : ICustomCommand\n    {\n        public byte[] Name { get; }\n\n        public readonly string NameStr;\n        public readonly byte id;\n        public readonly int arity;\n        public readonly Func<CustomTransactionProcedure> proc;\n\n        internal CustomTransaction(string name, byte id, int arity, Func<CustomTransactionProcedure> proc)\n        {\n            if (name == null)\n                throw new GarnetException(\"CustomTransaction name is null\");\n            NameStr = name.ToUpperInvariant();\n            this.Name = System.Text.Encoding.ASCII.GetBytes(NameStr);\n            this.id = id;\n            this.arity = arity;\n            this.proc = proc ?? throw new GarnetException(\"CustomTransactionProcedure is null\");\n        }\n    }\n}","sourceCodeStart":3,"sourceCodeEnd":29,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/server/Custom/CustomTransaction.cs#L3-L29","documentation":"Thrown by the CustomTransaction constructor when a module registers a custom transaction command with a null name string. The constructor (reached via CustomCommandManager.Register) guards explicitly because the next statement calls name.ToUpperInvariant(), which would otherwise throw an opaque NullReferenceException. It is a fail-fast programming-error check on the internal module-registration API surface.","triggerScenarios":"Module/extension code calls the internal CustomCommandManager.Register(string name, Func<CustomTransactionProcedure> proc, ...) overload (or the equivalent RegisterCustomTransaction provider path) passing null as the command name. Register is internal, so this only originates from Garnet-embedded hosts or module code, never from a RESP client.","commonSituations":"Embedding Garnet in a custom host and wiring a custom transaction whose name was read from config that resolved to null; a refactor that drops the name argument; a unit test that constructs CustomTransaction directly with a null literal.","solutions":["Pass a non-null, non-empty command name string to the registration call.","If the name comes from configuration, validate it is non-null before registering and log a clear startup error.","Search the call site 'new CustomTransaction(...)' and trace the name argument back to its source."],"exampleFix":"// before\nvar txnId = customCommandManager.Register(config.TransactionName, MakeProc, info);\n// after\nif (string.IsNullOrEmpty(config.TransactionName))\n    throw new InvalidOperationException(\"Custom transaction name missing in config.\");\nvar txnId = customCommandManager.Register(config.TransactionName, MakeProc, info);","handlingStrategy":"validation","validationCode":"// Validate before registering a custom transaction\nif (string.IsNullOrEmpty(name))\n    throw new ArgumentException(\"Custom transaction name must be non-null and non-empty.\", nameof(name));\ncustomCommandManager.Register(name, proc, commandInfo, commandDocs);","typeGuard":null,"tryCatchPattern":"try {\n    customCommandManager.Register(name, proc, info);\n} catch (GarnetException ex) when (ex.Message.Contains(\"name is null\")) {\n    logger.LogError(ex, \"Skipped custom transaction registration due to null name\");\n    throw; // registration was never going to succeed; fix the caller\n}","preventionTips":["Treat command names as required config and validate at startup before reaching the registration API.","Enable nullable reference types so the compiler flags null name arguments at the call site."],"tags":["custom-commands","registration","null-argument","module"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}