EllanJiang/GameFramework · error · GameFrameworkException

Event ' ' not exists specified handler.

Error message

Event '{0}' not exists specified handler.

What it means

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.

Solutions

  1. Ensure Unsubscribe receives the exact same delegate instance and id used in Subscribe (store the delegate in a field).
  2. Guard with a check or remove the redundant Unsubscribe call for handlers that were never subscribed.
  3. Verify the event id matches the one used at Subscribe time (use shared constants).
  4. Wrap in try-catch only if third-party code may unsubscribe handlers you don't own.
  5. Restructure so each Subscribe is paired exactly once with an Unsubscribe in symmetric lifecycle methods.

Example fix

// before
m_EventComponent.Unsubscribe(OpenUIEventArgs.EventId, OnOpenUI);
// after
private EventHandler<OpenUIEventArgs> m_OnOpenUI;
void Awake() { m_OnOpenUI = OnOpenUI; m_EventComponent.Subscribe(OpenUIEventArgs.EventId, m_OnOpenUI); }
void OnDestroy() { m_EventComponent.Unsubscribe(OpenUIEventArgs.EventId, m_OnOpenUI); }
Defensive patterns

Strategy: validation

Validate before calling

if (m_Handlers.TryGetValue(eventId, out var h) && ReferenceEquals(h, savedHandler)) { pool.Unsubscribe(eventId, savedHandler); }

Type guard

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));

Try / catch

try { pool.Unsubscribe(id, handler); } catch (GameFrameworkException) { /* handler was not subscribed; safe to ignore in cleanup */ }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of EllanJiang/GameFramework@d0c010b051 (2026-09-15). Data as JSON: /api/errors/65a3b9bb86292714. Report an issue: GitHub.

Appendix: source

Thrown at GameFramework/Base/EventPool/EventPool.cs:200

                    {
                        m_TempNodes.Add(cachedNode.Key, cachedNode.Value.Next);
                    }
                }

                if (m_TempNodes.Count > 0)
                {
                    foreach (KeyValuePair<object, LinkedListNode<EventHandler<T>>> cachedNode in m_TempNodes)
                    {
                        m_CachedNodes[cachedNode.Key] = cachedNode.Value;
                    }

                    m_TempNodes.Clear();
                }
            }

            if (!m_EventHandlers.Remove(id, handler))
            {
                throw new GameFrameworkException(Utility.Text.Format("Event '{0}' not exists specified handler.", id));
            }
        }

        /// <summary>
        /// 设置默认事件处理函数。
        /// </summary>
        /// <param name="handler">要设置的默认事件处理函数。</param>
        public void SetDefaultHandler(EventHandler<T> handler)
        {
            m_DefaultHandler = handler;
        }

        /// <summary>
        /// 抛出事件,这个操作是线程安全的,即使不在主线程中抛出,也可保证在主线程中回调事件处理函数,但事件会在抛出后的下一帧分发。
        /// </summary>
        /// <param name="sender">事件源。</param>
        /// <param name="e">事件参数。</param>
        public void Fire(object sender, T e)

View on GitHub (pinned to d0c010b051)