Cysharp/UniTask · error · InvalidOperationException

Can not trigger itself in iterating.

Error message

Can not trigger itself in iterating.

What it means

Thrown by TriggerEvent<T>.SetResult when iteratingNode is non-null, meaning the handler list is mid-iteration and a re-entrant call to SetResult was attempted. UniTask guards against recursive triggering because the linked-list traversal state (iteratingNode) would be corrupted, causing skipped or duplicate handler invocations. The struct deliberately prevents nested dispatch rather than risking undefined iteration behavior.

Source

Thrown at src/UniTask/Assets/Plugins/UniTask/Runtime/TriggerEvent.cs:38

    {
        ITriggerHandler<T> head; // head.prev is last
        ITriggerHandler<T> iteratingHead;
        ITriggerHandler<T> iteratingNode;

        void LogError(Exception ex)
        {
#if UNITY_2018_3_OR_NEWER
            UnityEngine.Debug.LogException(ex);
#else
            Console.WriteLine(ex);
#endif
        }

        public void SetResult(T value)
        {
            if (iteratingNode != null)
            {
                throw new InvalidOperationException("Can not trigger itself in iterating.");
            }

            var h = head;
            while (h != null)
            {
                iteratingNode = h;

                try
                {
                    h.OnNext(value);
                }
                catch (Exception ex)
                {
                    LogError(ex);
                    Remove(h);
                }

                // If `h` itself is removed by OnNext, h.Next is null.

View on GitHub (pinned to ceac8d6946)

Solutions

  1. Defer the re-entrant SetResult call by scheduling it for the next frame (UniTask.NextFrame or UniTask.Yield) so it runs after iteration completes
  2. Queue the value and flush it after the current iteration finishes by checking a flag in OnNext and calling SetResult from outside the callback
  3. Restructure the handler so it does not echo results back into the same TriggerEvent; use a separate TriggerEvent or a queue instead
  4. If you need pipelining, use UniTask's channel or IUniTaskAsyncEnumerable instead of direct TriggerEvent re-entrant calls

Example fix

// before
void OnNext(T value)
{
    triggerEvent.SetResult(transformedValue); // re-entrant -> throws
}

// after
void OnNext(T value)
{
    UniTask.NextFrame().ContinueWith(() => triggerEvent.SetResult(transformedValue)).Forget();
}
Defensive patterns

Strategy: validation

Validate before calling

// Before calling SetResult, ensure iteration is not active
// TriggerEvent is a struct; callers typically cannot inspect iteratingNode directly.
// Instead, defer re-entrant calls:
void SafeSetResult<T>(TriggerEvent<T> trigger, T value)
{
    UniTask.Yield().ContinueWith(() => trigger.SetResult(value)).Forget();
}

Try / catch

try
{
    triggerEvent.SetResult(value);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("iterating"))
{
    // defer to next frame
    UniTask.Yield().ContinueWith(() => triggerEvent.SetResult(value)).Forget();
}

Prevention

When it happens

Trigger: Inside an ITriggerHandler<T>.OnNext(T value) callback registered on a TriggerEvent, the handler synchronously calls SetResult on the same TriggerEvent instance. For example, an event handler that immediately tries to push a new value into the same event stream during dispatch.

Common situations: Building reactive event pipelines where a handler forwards or echoes events back to the source TriggerEvent. Using UniTask's player-loop trigger types (e.g., AsyncTrigger, MonoBehaviour triggers) where a callback inadvertently re-invokes the trigger. Custom ITriggerHandler implementations that chain events.

Related errors


AI-assisted analysis of Cysharp/UniTask@ceac8d6946 (2026-08-13). Data as JSON: /api/errors/cc6f811b38d2207b. Report an issue: GitHub.