Cysharp/UniTask · error · InvalidOperationException
Token version is not matched, can not await twice or get Sta
Error message
Token version is not matched, can not await twice or get Status after await.
What it means
Thrown by UniTaskCompletionSourceCore<TResult>.ValidateToken when the provided token does not match the current version. UniTask sources use a version (short token) to detect reuse after pooling or reset; an old token from a previous lifecycle is invalid. This typically indicates the UniTask was awaited or inspected after its source was recycled.
Source
Thrown at src/UniTask/Assets/Plugins/UniTask/Runtime/UniTaskCompletionSource.cs:304
{
// already running continuation in TrySet.
// It will cause call OnCompleted multiple time, invalid.
if (!ReferenceEquals(oldContinuation, UniTaskCompletionSourceCoreShared.s_sentinel))
{
throw new InvalidOperationException("Already continuation registered, can not await twice or get Status after await.");
}
continuation(state);
}
}
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ValidateToken(short token)
{
if (token != version)
{
throw new InvalidOperationException("Token version is not matched, can not await twice or get Status after await.");
}
}
}
internal static class UniTaskCompletionSourceCoreShared // separated out of generic to avoid unnecessary duplication
{
internal static readonly Action<object> s_sentinel = CompletionSentinel;
private static void CompletionSentinel(object _) // named method to aid debugging
{
throw new InvalidOperationException("The sentinel delegate should never be invoked.");
}
}
public class AutoResetUniTaskCompletionSource : IUniTaskSource, ITaskPoolNode<AutoResetUniTaskCompletionSource>, IPromise
{
static TaskPool<AutoResetUniTaskCompletionSource> pool;
AutoResetUniTaskCompletionSource nextNode;View on GitHub (pinned to ceac8d6946)
Solutions
- Await each UniTask exactly once and immediately
- Do not cache UniTask or its token across frames; await it where it is created
- If you need a reusable awaitable, use UniTaskCompletionSource (not pooled) or AutoResetUniTaskCompletionSource with proper lifecycle management
- Ensure you do not call GetResult/GetStatus after the task has been consumed
Example fix
// before var task = source.Task; await task; var status = task.GetStatus(oldToken); // throws: token mismatch // after var task = source.Task; await task; // do not touch the task after await
Defensive patterns
Strategy: validation
Validate before calling
// Ensure token is current before use // In practice: never hold a UniTask across pool resets // Always await exactly once where the UniTask is created
Prevention
- Await each UniTask exactly once immediately after creation
- Never cache UniTask or its token beyond the first await
- For reusable awaitables, use non-pooled UniTaskCompletionSource
- Audit custom code that stores short tokens for later use
When it happens
Trigger: Holding a UniTask and its token across a source reset/pool return, then calling GetResult/GetStatus/OnCompleted with the stale token. Awaiting a UniTask whose source has already been returned to the pool from a prior await.
Common situations: Awaiting a UniTask twice (second await uses a stale token after pool return). Caching a UniTask field that is reused across frames where the underlying source is pooled. Custom code that stores the token from a pooled source and uses it later.
Related errors
- Already continuation registered, can not await twice or get
- Not yet completed, UniTask only allow to use await.
- continuation
- not yet completed.
- Can not trigger itself in iterating.
AI-assisted analysis of Cysharp/UniTask@ceac8d6946 (2026-08-13).
Data as JSON: /api/errors/862af9c2a20f5c0d.
Report an issue: GitHub.