microsoft/garnet · error · GarnetException

CustomTransaction name is null

Error message

CustomTransaction name is null

What it means

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.

Source

Thrown at libs/server/Custom/CustomTransaction.cs:21

using System;
using Garnet.common;

namespace Garnet.server
{
    class CustomTransaction : ICustomCommand
    {
        public byte[] Name { get; }

        public readonly string NameStr;
        public readonly byte id;
        public readonly int arity;
        public readonly Func<CustomTransactionProcedure> proc;

        internal CustomTransaction(string name, byte id, int arity, Func<CustomTransactionProcedure> proc)
        {
            if (name == null)
                throw new GarnetException("CustomTransaction name is null");
            NameStr = name.ToUpperInvariant();
            this.Name = System.Text.Encoding.ASCII.GetBytes(NameStr);
            this.id = id;
            this.arity = arity;
            this.proc = proc ?? throw new GarnetException("CustomTransactionProcedure is null");
        }
    }
}

View on GitHub (pinned to 951b0fc683)

Solutions

  1. Pass a non-null, non-empty command name string to the registration call.
  2. If the name comes from configuration, validate it is non-null before registering and log a clear startup error.
  3. Search the call site 'new CustomTransaction(...)' and trace the name argument back to its source.

Example fix

// before
var txnId = customCommandManager.Register(config.TransactionName, MakeProc, info);
// after
if (string.IsNullOrEmpty(config.TransactionName))
    throw new InvalidOperationException("Custom transaction name missing in config.");
var txnId = customCommandManager.Register(config.TransactionName, MakeProc, info);
Defensive patterns

Strategy: validation

Validate before calling

// Validate before registering a custom transaction
if (string.IsNullOrEmpty(name))
    throw new ArgumentException("Custom transaction name must be non-null and non-empty.", nameof(name));
customCommandManager.Register(name, proc, commandInfo, commandDocs);

Try / catch

try {
    customCommandManager.Register(name, proc, info);
} catch (GarnetException ex) when (ex.Message.Contains("name is null")) {
    logger.LogError(ex, "Skipped custom transaction registration due to null name");
    throw; // registration was never going to succeed; fix the caller
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13). Data as JSON: /api/errors/2a3411aea0f85628. Report an issue: GitHub.