{"record":{"id":"1b125e1738fd3ff5","repo":"dotnet/reactive","slug":"strings-linq-event-must-return-void","errorCode":null,"errorMessage":"Strings_Linq.EVENT_MUST_RETURN_VOID","messagePattern":"Strings_Linq\\.EVENT_MUST_RETURN_VOID","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive/Internal/ReflectionUtils.cs","lineNumber":92,"sourceCode":"\n            if (parameters.Length != 2)\n            {\n                throw new InvalidOperationException(Strings_Linq.EVENT_PATTERN_REQUIRES_TWO_PARAMETERS);\n            }\n\n            if (!typeof(TSender).IsAssignableFrom(parameters[0].ParameterType))\n            {\n                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Strings_Linq.EVENT_SENDER_NOT_ASSIGNABLE, typeof(TSender).FullName));\n            }\n\n            if (!typeof(TEventArgs).IsAssignableFrom(parameters[1].ParameterType))\n            {\n                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Strings_Linq.EVENT_ARGS_NOT_ASSIGNABLE, typeof(TEventArgs).FullName));\n            }\n\n            if (invokeMethod.ReturnType != typeof(void))\n            {\n                throw new InvalidOperationException(Strings_Linq.EVENT_MUST_RETURN_VOID);\n            }\n        }\n\n        /// <summary>\n        /// Determine whether a type represents a WinRT event registration token\n        /// (https://learn.microsoft.com/en-us/uwp/api/windows.foundation.eventregistrationtoken).\n        /// </summary>\n        /// <param name=\"t\">The type to check.</param>\n        /// <returns>True if this represents a WinRT event registration token</returns>\n        /// <remarks>\n        /// <para>\n        /// We used to perform a simple comparison with typeof(EventRegistrationToken), but the\n        /// introduction of C#/WinRT has made this problematic. Before C#/WinRT, the .NET\n        /// projection of WinRT's Windows.Foundation.EventRegistrationToken type was\n        /// System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken. But that type is\n        /// associated with the old WinRT interop mechanisms in which the CLR works directly with\n        /// WinMD. That was how it worked up as far as .NET Core 3.1, and it's still how .NET\n        /// Framework works, but this direct WinMD support was removed in .NET 5.0.","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Internal/ReflectionUtils.cs#L74-L110","documentation":"Event delegates must return void; the .NET event pattern defines handlers with a void return. If the delegate's Invoke method returns a non-void type, Rx cannot treat it as an event handler and GetEventMethods throws InvalidOperationException.","triggerScenarios":"Passing a delegate type (e.g. Func<int, string, bool>) to FromEventPattern/FromEvent where the handler returns a value.","commonSituations":"Callback-style APIs mistakenly exposed as events; custom delegates with return values used for observer-style notifications; hand-rolled add/remove accessors accepting value-returning delegates.","solutions":["Use a void-returning delegate (Action/EventHandler style) for the event.","If the API returns values, model it as a method call or use Observable.FromAsyncPattern-style adapters instead of FromEventPattern.","Wrap the value-returning callback manually with Observable.Create."],"exampleFix":"// before\npublic delegate bool Validate(object sender, EventArgs e);\n// after\npublic delegate void Validate(object sender, EventArgs e); // or EventHandler","handlingStrategy":"validation","validationCode":"var invoke = delegateType.GetMethod(\"Invoke\");\nif (invoke?.ReturnType != typeof(void))\n    throw new ArgumentException(\"Event delegates must return void\");","typeGuard":null,"tryCatchPattern":"try\n{\n    var src = Observable.FromEventPattern(target, eventName);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"void\"))\n{\n    // adapt the callback-style API with Observable.Create instead\n}","preventionTips":["Only declare void-returning delegates for events.","Treat value-returning callbacks as methods/async calls, not events.","Lint custom delegate declarations used as event types."],"tags":["delegates","events","invalid-argument"],"backgroundTag":"invalid-argument-format","analyzedSha":"94b5d5ab912789f5abe9a72138a25bbd716fe59c","analyzedAt":"2026-09-15T02:26:24.759Z","contentChangedAt":"2026-09-15T02:26:24.759Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}