{"record":{"id":"0b42d27d8144051f","repo":"microsoft/garnet","slug":"value-cannot-be-null-parameter-functions","errorCode":null,"errorMessage":"Value cannot be null. (Parameter 'functions')","messagePattern":"Value cannot be null\\. \\(Parameter 'functions'\\)","errorType":"validation","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"libs/storage/Tsavorite/cs/src/core/ClientSession/ManageClientSessions.cs","lineNumber":37,"sourceCode":"        /// <param name=\"functions\">Callback functions</param>\n        /// <param name=\"enableConsistentRead\">Enable consistent read context</param>\n        /// <param name=\"readCopyOptions\"><see cref=\"ReadCopyOptions\"/> for this session; override those specified at TsavoriteKV level, and may be overridden on individual Read operations</param>\n        /// <param name=\"initialIORecordSize\">Initial IO record size for disk reads in this session;\n        ///     <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>\n        /// <returns>Session instance</returns>\n        public ClientSession<TKey, TInput, TOutput, TContext, TFunctions, TStoreFunctions, TAllocator> NewSession<TKey, TInput, TOutput, TContext, TFunctions>(\n            TFunctions functions,\n            bool enableConsistentRead = false,\n            ReadCopyOptions readCopyOptions = default,\n            int initialIORecordSize = KVSettings.UseDefaultInitialIORecordSize)\n            where TKey : IKey\n#if NET9_0_OR_GREATER\n                , allows ref struct\n#endif\n            where TFunctions : ISessionFunctions<TInput, TOutput, TContext>\n        {\n            if (functions == null)\n                throw new ArgumentNullException(nameof(functions));\n\n            int sessionID = Interlocked.Increment(ref maxSessionID);\n            var ctx = new TsavoriteExecutionContext<TInput, TOutput, TContext>(sessionID);\n            ctx.MergeReadCopyOptions(ReadCopyOptions, readCopyOptions);\n            ctx.InitialIORecordSize = initialIORecordSize;\n\n            if (RevivificationManager.IsEnabled)\n            {\n                if (_activeSessions == null)\n                    _ = Interlocked.CompareExchange(ref _activeSessions, [], null);\n            }\n            var session = new ClientSession<TKey, TInput, TOutput, TContext, TFunctions, TStoreFunctions, TAllocator>(this, ctx, functions, enableConsistentRead);\n            lock (_activeSessions)\n                _activeSessions.Add(sessionID, new SessionInfo { session = session, isActive = true });\n            return session;\n        }\n\n        /// <summary>","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/storage/Tsavorite/cs/src/core/ClientSession/ManageClientSessions.cs#L19-L55","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Ensure the TFunctions argument passed to NewSession is a properly constructed instance before the call.","If using DI, register the ISessionFunctions implementation in the container so it resolves to a non-null instance.","Add a null check or ArgumentNullException.ThrowIfNull in your own session-creation wrapper before delegating to NewSession."],"exampleFix":"// before (throws if factory.Create() returns null)\nvar session = store.NewSession<TKey, TInput, TOutput, TContext, MyFunctions>(factory.Create());\n\n// after\nvar functions = factory.Create() ?? throw new InvalidOperationException(\"Functions factory returned null\");\nvar session = store.NewSession<TKey, TInput, TOutput, TContext, MyFunctions>(functions);","handlingStrategy":"validation","validationCode":"// Validate functions before creating the session\nArgumentNullException.ThrowIfNull(functions);\nvar session = store.NewSession<TKey, TInput, TOutput, TContext, TFunctions>(functions);","typeGuard":null,"tryCatchPattern":"// Wrap session creation if the source of functions is unreliable\ntry\n{\n    var session = store.NewSession<...>(functions);\n}\ncatch (ArgumentNullException ex) when (ex.ParamName == \"functions\")\n{\n    logger.LogError(\"Session functions were not provided\");\n    throw;\n}","preventionTips":["Register ISessionFunctions implementations in your DI container so they always resolve to non-null instances.","Use ArgumentNullException.ThrowIfNull(functions) before calling NewSession in your own factory methods.","Avoid conditional assignment of the functions variable — initialize it at declaration."],"tags":["tsavorite","session-creation","argument-null","validation","session-functions","csharp"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}