{"record":{"id":"65a3b9bb86292714","repo":"EllanJiang/GameFramework","slug":"event-0-not-exists-specified-handler","errorCode":null,"errorMessage":"Event '{0}' not exists specified handler.","messagePattern":"Event '(.+?)' not exists specified handler\\.","errorType":"exception","errorClass":"GameFrameworkException","httpStatus":null,"severity":"error","filePath":"GameFramework/Base/EventPool/EventPool.cs","lineNumber":200,"sourceCode":"                    {\n                        m_TempNodes.Add(cachedNode.Key, cachedNode.Value.Next);\n                    }\n                }\n\n                if (m_TempNodes.Count > 0)\n                {\n                    foreach (KeyValuePair<object, LinkedListNode<EventHandler<T>>> cachedNode in m_TempNodes)\n                    {\n                        m_CachedNodes[cachedNode.Key] = cachedNode.Value;\n                    }\n\n                    m_TempNodes.Clear();\n                }\n            }\n\n            if (!m_EventHandlers.Remove(id, handler))\n            {\n                throw new GameFrameworkException(Utility.Text.Format(\"Event '{0}' not exists specified handler.\", id));\n            }\n        }\n\n        /// <summary>\n        /// 设置默认事件处理函数。\n        /// </summary>\n        /// <param name=\"handler\">要设置的默认事件处理函数。</param>\n        public void SetDefaultHandler(EventHandler<T> handler)\n        {\n            m_DefaultHandler = handler;\n        }\n\n        /// <summary>\n        /// 抛出事件，这个操作是线程安全的，即使不在主线程中抛出，也可保证在主线程中回调事件处理函数，但事件会在抛出后的下一帧分发。\n        /// </summary>\n        /// <param name=\"sender\">事件源。</param>\n        /// <param name=\"e\">事件参数。</param>\n        public void Fire(object sender, T e)","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/EllanJiang/GameFramework/blob/d0c010b05167c58e92350449d04864a91ca13fd2/GameFramework/Base/EventPool/EventPool.cs#L182-L218","documentation":"EventPool.Unsubscribe throws this when Remove(id, handler) returns false, meaning no handler exactly matching the given event id and delegate reference is currently subscribed. The library treats unsubscribing a non-existent handler as a programming mistake rather than a no-op, so it throws GameFrameworkException to surface the mismatch early.","triggerScenarios":"Calling Unsubscribe(int id, EventHandler<T> handler) with an id that has no subscriber, or with a handler delegate instance that was never subscribed (or a different delegate instance than the one passed to Subscribe). Also occurs when Subscribe was skipped, the handler was already unsubscribed, or the pool mode discards handlers.","commonSituations":"Double-calling Unsubscribe in cleanup code; subscribing with a lambda (new delegate each time) but unsubscribing with a different lambda or method group; UI/actor Destroy order unsubscribing before Subscribe ran; event id constants changed between code paths.","solutions":["Ensure Unsubscribe receives the exact same delegate instance and id used in Subscribe (store the delegate in a field).","Guard with a check or remove the redundant Unsubscribe call for handlers that were never subscribed.","Verify the event id matches the one used at Subscribe time (use shared constants).","Wrap in try-catch only if third-party code may unsubscribe handlers you don't own.","Restructure so each Subscribe is paired exactly once with an Unsubscribe in symmetric lifecycle methods."],"exampleFix":"// before\nm_EventComponent.Unsubscribe(OpenUIEventArgs.EventId, OnOpenUI);\n// after\nprivate EventHandler<OpenUIEventArgs> m_OnOpenUI;\nvoid Awake() { m_OnOpenUI = OnOpenUI; m_EventComponent.Subscribe(OpenUIEventArgs.EventId, m_OnOpenUI); }\nvoid OnDestroy() { m_EventComponent.Unsubscribe(OpenUIEventArgs.EventId, m_OnOpenUI); }","handlingStrategy":"validation","validationCode":"if (m_Handlers.TryGetValue(eventId, out var h) && ReferenceEquals(h, savedHandler)) { pool.Unsubscribe(eventId, savedHandler); }","typeGuard":"static bool IsSubscribed<T>(EventPool<T> pool, int id, EventHandler<T> handler) where T : GameFrameworkEventArgs => handler != null && /* track your own subscriptions */ SubscribedIds.Contains((id, handler));","tryCatchPattern":"try { pool.Unsubscribe(id, handler); } catch (GameFrameworkException) { /* handler was not subscribed; safe to ignore in cleanup */ }","preventionTips":["Store the handler delegate in a field; never subscribe/unsubscribe with lambdas","Pair every Subscribe with exactly one Unsubscribe in symmetric lifecycle methods","Use shared event id constants instead of magic numbers","Do not call Unsubscribe for handlers that may not be subscribed"],"tags":["event-pool","unsubscribe","gameframework","csharp"],"backgroundTag":"resource-not-found","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"}