{"record":{"id":"d3b914c4d0c88328","repo":"Cysharp/UniTask","slug":"continuation-is-already-registered","errorCode":null,"errorMessage":"continuation is already registered.","messagePattern":"continuation is already registered\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/Error.cs","lineNumber":69,"sourceCode":"        }\n\n        [MethodImpl(MethodImplOptions.NoInlining)]\n        public static T ThrowNotYetCompleted<T>()\n        {\n            throw new InvalidOperationException(\"Not yet completed.\");\n        }\n\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        public static void ThrowWhenContinuationIsAlreadyRegistered<T>(T continuationField)\n          where T : class\n        {\n            if (continuationField != null) ThrowInvalidOperationExceptionCore(\"continuation is already registered.\");\n        }\n\n        [MethodImpl(MethodImplOptions.NoInlining)]\n        static void ThrowInvalidOperationExceptionCore(string message)\n        {\n            throw new InvalidOperationException(message);\n        }\n\n        [MethodImpl(MethodImplOptions.NoInlining)]\n        public static void ThrowOperationCanceledException()\n        {\n            throw new OperationCanceledException();\n        }\n    }\n}\n\n","sourceCodeStart":51,"sourceCodeEnd":80,"githubUrl":"https://github.com/Cysharp/UniTask/blob/ceac8d6946b1125fe782cd171fbcb245b567dbf9/src/UniTask/Assets/Plugins/UniTask/Runtime/Internal/Error.cs#L51-L80","documentation":"Thrown by Error.ThrowWhenContinuationIsAlreadyRegistered when the continuation field on a UniTask source is already non-null. UniTask sources support exactly one continuation (single-awaiter semantics); a second registration indicates the same source was awaited from two concurrent paths. This is by design — UniTask is a zero-allocation single-shot awaitable, not a multi-cast Task.","triggerScenarios":"Awaiting the same UniTaskManualSource or configured UniTask source from two different async methods simultaneously. Or calling the continuation-registration API twice on the same token.","commonSituations":"Storing a UniTaskCompletionSource in a shared field and awaiting it from multiple callers. A race in cancellation logic that registers a second continuation. Incorrectly pooling/reusing a task source across multiple awaits.","solutions":["Treat each UniTask source as single-use: one awaiter only. Create a fresh source for each consumer.","If multiple consumers need notification, use a separate mechanism (e.g., channel, CancellationTokenSource, or UniTask.WhenAll on individual tasks).","Audit shared fields holding UniTask sources and ensure they are not awaited concurrently."],"exampleFix":"// before\nvar tcs = new UniTaskCompletionSource();\nasync UniTask A() => await tcs.Task;\nasync UniTask B() => await tcs.Task;\n// A(); B();  // throws: continuation already registered\n\n// after\n// Create a separate source per consumer, or use WhenAll pattern\nvar tcs1 = new UniTaskCompletionSource();\nvar tcs2 = new UniTaskCompletionSource();\nawait UniTask.WhenAll(tcs1.Task, tcs2.Task);","handlingStrategy":"validation","validationCode":"// Do not share UniTask sources across multiple awaiters.\n// Each UniTaskCompletionSource is single-use:\n// BAD:  shared field awaited by two callers\n// GOOD: create a new source per awaiter","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat every UniTask/UniTaskCompletionSource as single-awaiter — never store one in a shared field that multiple async methods await.","For multi-consumer notification, use channels or multiple independent sources.","Do not reuse or pool UniTask sources across awaits."],"tags":["async","continuation","single-awaiter","concurrency"],"backgroundTag":null,"analyzedSha":"ceac8d6946b1125fe782cd171fbcb245b567dbf9","analyzedAt":"2026-08-13T19:16:39.025Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}