{"record":{"id":"df3ef33b51001f9a","repo":"dotnet/reactive","slug":"thrown-when-finallyaction-is-null-argumentnullexception","errorCode":null,"errorMessage":"Thrown when finallyAction is null (ArgumentNullException, param name: finallyAction)","messagePattern":"Thrown when finallyAction is null \\(ArgumentNullException, param name: finallyAction\\)","errorType":"validation","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"Ix.NET/Source/System.Interactive/System/Linq/Operators/Finally.cs","lineNumber":23,"sourceCode":"using System.Collections.Generic;\n\nnamespace System.Linq\n{\n    public static partial class EnumerableEx\n    {\n        /// <summary>\n        /// Creates a sequence whose termination or disposal of an enumerator causes a finally action to be executed.\n        /// </summary>\n        /// <typeparam name=\"TSource\">Source sequence element type.</typeparam>\n        /// <param name=\"source\">Source sequence.</param>\n        /// <param name=\"finallyAction\">Action to run upon termination of the sequence, or when an enumerator is disposed.</param>\n        /// <returns>Source sequence with guarantees on the invocation of the finally action.</returns>\n        public static IEnumerable<TSource> Finally<TSource>(this IEnumerable<TSource> source, Action finallyAction)\n        {\n            if (source == null)\n                throw new ArgumentNullException(nameof(source));\n            if (finallyAction == null)\n                throw new ArgumentNullException(nameof(finallyAction));\n\n            return FinallyCore(source, finallyAction);\n        }\n\n        private static IEnumerable<TSource> FinallyCore<TSource>(IEnumerable<TSource> source, Action finallyAction)\n        {\n            try\n            {\n                foreach (var item in source)\n                {\n                    yield return item;\n                }\n            }\n            finally\n            {\n                finallyAction();\n            }\n        }","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Ix.NET/Source/System.Interactive/System/Linq/Operators/Finally.cs#L5-L41","documentation":"Finally throws ArgumentNullException when finallyAction is null. The whole point of the operator is to invoke this action reliably, so a null action is invalid and rejected eagerly with paramName 'finallyAction'.","triggerScenarios":"Calling Finally(source, null), e.g. a cleanup Action resolved from an optional member or DI registration that is missing.","commonSituations":"Optional cleanup callback not registered; passing a null delegate from configuration; refactoring removed the handler but kept the call.","solutions":["Provide a no-op () => {} when cleanup is genuinely optional.","Ensure the cleanup delegate is registered/initialized before the call.","Guard the delegate source with a default."],"exampleFix":"// before\nvar seq = source.Finally(cleanup); // cleanup may be null\n// after\nvar seq = source.Finally(cleanup ?? (() => { }));","handlingStrategy":"validation","validationCode":"if (finallyAction is null) throw new ArgumentNullException(nameof(finallyAction));\nFinally(source, finallyAction ?? (() => { }));","typeGuard":"bool HasAction(Action a) => a is not null;","tryCatchPattern":"try { var q = source.Finally(action); }\ncatch (ArgumentNullException ex) when (ex.ParamName == \"finallyAction\") { /* register or default the action */ }","preventionTips":["Register cleanup handlers during initialization, not lazily.","Default optional actions to no-ops.","Verify DI registrations for cleanup delegates.","Avoid obj?.Handler forms that yield null delegates."],"tags":["null-argument","argument-validation","delegate"],"backgroundTag":"null-argument","analyzedSha":"94b5d5ab912789f5abe9a72138a25bbd716fe59c","analyzedAt":"2026-09-15T02:26:24.759Z","contentChangedAt":"2026-09-15T02:26:24.759Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}