microsoft/garnet · error · ArgumentNullException

Value cannot be null. (Parameter 'functions')

Error message

Value cannot be null. (Parameter 'functions')

What it means

NewSession requires a non-null TFunctions (ISessionFunctions) callback instance because every Tsavorite operation (Read, Upsert, RMW, Delete, compaction callbacks) dispatches through it. Passing null would cause NullReferenceException deep inside the operation pipeline, so the constructor validates upfront with a standard ArgumentNullException. The check uses nameof(functions) so the parameter name appears in the exception.

Source

Thrown at libs/storage/Tsavorite/cs/src/core/ClientSession/ManageClientSessions.cs:37

        /// <param name="functions">Callback functions</param>
        /// <param name="enableConsistentRead">Enable consistent read context</param>
        /// <param name="readCopyOptions"><see cref="ReadCopyOptions"/> for this session; override those specified at TsavoriteKV level, and may be overridden on individual Read operations</param>
        /// <param name="initialIORecordSize">Initial IO record size for disk reads in this session;
        ///     <see cref="KVSettings.UseDefaultInitialIORecordSize"/> means inherit from the store-level setting, and may be overridden on individual Read operations via <see cref="ReadOptions.InitialIORecordSize"/>.</param>
        /// <returns>Session instance</returns>
        public ClientSession<TKey, TInput, TOutput, TContext, TFunctions, TStoreFunctions, TAllocator> NewSession<TKey, TInput, TOutput, TContext, TFunctions>(
            TFunctions functions,
            bool enableConsistentRead = false,
            ReadCopyOptions readCopyOptions = default,
            int initialIORecordSize = KVSettings.UseDefaultInitialIORecordSize)
            where TKey : IKey
#if NET9_0_OR_GREATER
                , allows ref struct
#endif
            where TFunctions : ISessionFunctions<TInput, TOutput, TContext>
        {
            if (functions == null)
                throw new ArgumentNullException(nameof(functions));

            int sessionID = Interlocked.Increment(ref maxSessionID);
            var ctx = new TsavoriteExecutionContext<TInput, TOutput, TContext>(sessionID);
            ctx.MergeReadCopyOptions(ReadCopyOptions, readCopyOptions);
            ctx.InitialIORecordSize = initialIORecordSize;

            if (RevivificationManager.IsEnabled)
            {
                if (_activeSessions == null)
                    _ = Interlocked.CompareExchange(ref _activeSessions, [], null);
            }
            var session = new ClientSession<TKey, TInput, TOutput, TContext, TFunctions, TStoreFunctions, TAllocator>(this, ctx, functions, enableConsistentRead);
            lock (_activeSessions)
                _activeSessions.Add(sessionID, new SessionInfo { session = session, isActive = true });
            return session;
        }

        /// <summary>

View on GitHub (pinned to 951b0fc683)

Solutions

  1. Ensure the TFunctions argument passed to NewSession is a properly constructed instance before the call.
  2. If using DI, register the ISessionFunctions implementation in the container so it resolves to a non-null instance.
  3. Add a null check or ArgumentNullException.ThrowIfNull in your own session-creation wrapper before delegating to NewSession.

Example fix

// before (throws if factory.Create() returns null)
var session = store.NewSession<TKey, TInput, TOutput, TContext, MyFunctions>(factory.Create());

// after
var functions = factory.Create() ?? throw new InvalidOperationException("Functions factory returned null");
var session = store.NewSession<TKey, TInput, TOutput, TContext, MyFunctions>(functions);
Defensive patterns

Strategy: validation

Validate before calling

// Validate functions before creating the session
ArgumentNullException.ThrowIfNull(functions);
var session = store.NewSession<TKey, TInput, TOutput, TContext, TFunctions>(functions);

Try / catch

// Wrap session creation if the source of functions is unreliable
try
{
    var session = store.NewSession<...>(functions);
}
catch (ArgumentNullException ex) when (ex.ParamName == "functions")
{
    logger.LogError("Session functions were not provided");
    throw;
}

Prevention

When it happens

Trigger: Calling tsavoriteKV.NewSession<...>(functions: null) or a generic factory that resolves the functions argument to null (e.g. a DI container that failed to register the ISessionFunctions, or a default(TFunctions) that is a null reference for class-based functions).

Common situations: Dependency injection misconfiguration where the ISessionFunctions service is not registered; conditional session creation where the functions variable is assigned in a branch that was not taken; passing a struct functions type via a nullable wrapper that yields null.

Related errors


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