{"record":{"id":"f220fad350cb96da","repo":"EllanJiang/GameFramework","slug":"event-handler-is-invalid","errorCode":null,"errorMessage":"Event handler is invalid.","messagePattern":"Event handler is invalid\\.","errorType":"exception","errorClass":"GameFrameworkException","httpStatus":null,"severity":"error","filePath":"GameFramework/Base/EventPool/EventPool.cs","lineNumber":129,"sourceCode":"            if (m_EventHandlers.TryGetValue(id, out range))\n            {\n                return range.Count;\n            }\n\n            return 0;\n        }\n\n        /// <summary>\n        /// 检查是否存在事件处理函数。\n        /// </summary>\n        /// <param name=\"id\">事件类型编号。</param>\n        /// <param name=\"handler\">要检查的事件处理函数。</param>\n        /// <returns>是否存在事件处理函数。</returns>\n        public bool Check(int id, EventHandler<T> handler)\n        {\n            if (handler == null)\n            {\n                throw new GameFrameworkException(\"Event handler is invalid.\");\n            }\n\n            return m_EventHandlers.Contains(id, handler);\n        }\n\n        /// <summary>\n        /// 订阅事件处理函数。\n        /// </summary>\n        /// <param name=\"id\">事件类型编号。</param>\n        /// <param name=\"handler\">要订阅的事件处理函数。</param>\n        public void Subscribe(int id, EventHandler<T> handler)\n        {\n            if (handler == null)\n            {\n                throw new GameFrameworkException(\"Event handler is invalid.\");\n            }\n\n            if (!m_EventHandlers.Contains(id))","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/EllanJiang/GameFramework/blob/d0c010b05167c58e92350449d04864a91ca13fd2/GameFramework/Base/EventPool/EventPool.cs#L111-L147","documentation":"EventPool.Check validates that the supplied EventHandler<T> delegate is not null before querying whether the event pool already contains a handler for the given event id. If handler is null, the library cannot perform a meaningful containment check, so it throws GameFrameworkException('Event handler is invalid.'). This is a fail-fast guard protecting the internal m_EventHandlers collection from null delegates.","triggerScenarios":"Calling eventPool.Check(id, null) — most often when the handler was stored in a field/variable that was never assigned, or a method returns a delegate that is null (e.g. a target object method that no longer exists so the delegate creation yielded null after refactors or conditional initialization).","commonSituations":"Passing a class member method group whose instance is conditionally constructed; refactoring renamed a handler method and a null-conditional delegate path silently returns null; passing the result of a lookup (dictionary/registry of handlers) that missed; C# EventHandler<T> field left at default before subscription setup code runs.","solutions":["Verify the handler delegate is assigned before calling Check; add a null check on the caller side.","If the handler comes from a registry/dictionary lookup, confirm the lookup key exists and fall back or log before calling Check.","Fix the initialization order so the handler field/method group is created before any Check/Subscribe/Unsubscribe calls.","Wrap subscription logic in a guard: if (handler == null) return/log instead of delegating to Check."],"exampleFix":"// before\nif (m_EventPool.Check((int)GameEvent.MyEvent, MyHandler)) { ... }\n// after\nif (MyHandler != null && m_EventPool.Check((int)GameEvent.MyEvent, MyHandler)) { ... }","handlingStrategy":"validation","validationCode":"if (handler == null) throw new ArgumentNullException(nameof(handler));\nbool exists = eventPool.Check(id, handler);","typeGuard":"bool IsValidHandler<TArgs>(EventHandler<TArgs> handler) where TArgs : GameFrameworkEventArgs => handler is not null;","tryCatchPattern":"try\n{\n    bool exists = eventPool.Check(id, handler);\n}\ncatch (GameFrameworkException ex) when (ex.Message == \"Event handler is invalid.\")\n{\n    // handler was null — log and skip\n}","preventionTips":["Never pass unassigned delegate fields to Check/Subscribe/Unsubscribe.","Resolve handlers from registries only after verifying the lookup succeeded.","Initialize handler delegates in Awake/constructor before any event pool usage.","Centralize subscription helper methods that null-check once for the whole codebase."],"tags":["event-pool","null-handler","argument-validation"],"backgroundTag":"null-argument","analyzedSha":"d0c010b05167c58e92350449d04864a91ca13fd2","analyzedAt":"2026-09-15T13:37:15.352Z","contentChangedAt":"2026-09-15T13:37:15.352Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}