egametang/ET · error · InvalidOperationException
TaskT_TransitionToFinal_AlreadyCompleted
Error message
TaskT_TransitionToFinal_AlreadyCompleted
What it means
Thrown by ETTask.SetResult() when the task's state is no longer Pending — i.e. it was already completed (Succeeded or Faulted). ETTask objects may be pooled/reused, so completing a task twice corrupts state and is rejected with InvalidOperationException. Usually indicates double-completion, reuse-after-recycle, or a race where two paths resolve the same task.
Source
Thrown at Packages/cn.etetet.core/Scripts/Core/Share/ETTask/ETTask.cs:212
this.Recycle();
break;
case AwaiterStatus.Faulted:
ExceptionDispatchInfo c = this.callback as ExceptionDispatchInfo;
this.callback = null;
this.Recycle();
c?.Throw();
break;
default:
throw new NotSupportedException("ETTask does not allow call GetResult directly when task not completed. Please use 'await'.");
}
}
[DebuggerHidden]
public void SetResult()
{
if (this.state != AwaiterStatus.Pending)
{
throw new InvalidOperationException("TaskT_TransitionToFinal_AlreadyCompleted");
}
this.state = AwaiterStatus.Succeeded;
Action c = this.callback as Action;
this.callback = null;
c?.Invoke();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[DebuggerHidden]
public void SetException(Exception e)
{
if (this.state != AwaiterStatus.Pending)
{
throw new InvalidOperationException("TaskT_TransitionToFinal_AlreadyCompleted");
}
View on GitHub (pinned to 5cab01f7a8)
Solutions
- Guard completions: if (task.state == AwaiterStatus.Pending) task.SetResult();
- Ensure exactly one code path owns task completion; route cancellation through the token, not a second SetResult/SetException.
- Avoid handing the same ETTask instance to multiple resolvers; do not reuse pooled tasks before reset.
Example fix
// before task.SetResult(); task.SetResult(); // throws TaskT_TransitionToFinal_AlreadyCompleted // after task.SetResult(); // ensure no second completion; if needed, check state first.
Defensive patterns
Strategy: validation
Validate before calling
// Complete at most once. if (task.state == AwaiterStatus.Pending) task.SetResult();
Try / catch
try { task.SetResult(); } catch (InvalidOperationException ex) when (ex.Message.Contains("AlreadyCompleted")) { /* log duplicate completion; ignore */ } Prevention
- Assign a single owner responsible for task completion.
- Route cancellation through the token, not a second SetException/SetResult.
- Do not reuse pooled ETTask instances before they are reset.
When it happens
Trigger: Calling SetResult twice on the same ETTask; resolving a task both via SetResult and SetException; a recycled/pooled task being completed by stale code; concurrent fibers resolving the same task.
Common situations: Object-pool reuse without resetting state; fire-and-forget code paths (.Coroutine()) that also complete the task; cancellation token firing after SetResult; bugs where an awaiter callback and the producer both complete the task.
Related errors
- ETTask does not allow call GetResult directly when task not
- add cancel token is null
- service discovery agent disposed
- btenv not found key: {key} {typeof(T).FullName}
- 不能把{value.GetType()}转换为{typeof (T)}
AI-assisted analysis of egametang/ET@5cab01f7a8 (2026-08-13).
Data as JSON: /api/errors/c8161ce3e6f64d31.
Report an issue: GitHub.