{"record":{"id":"001a387ff680dfb4","repo":"Cysharp/UniTask","slug":"attempting-to-use-an-invalid-operation-handle","errorCode":null,"errorMessage":"Attempting to use an invalid operation handle","messagePattern":"Attempting to use an invalid operation handle","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables/AddressablesAsyncExtensions.cs","lineNumber":288,"sourceCode":"#region AsyncOperationHandle_T\n\n        public static UniTask<T>.Awaiter GetAwaiter<T>(this AsyncOperationHandle<T> handle)\n        {\n            return ToUniTask(handle).GetAwaiter();\n        }\n\n        public static UniTask<T> WithCancellation<T>(this AsyncOperationHandle<T> handle, CancellationToken cancellationToken, bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)\n        {\n            return ToUniTask(handle, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately, autoReleaseWhenCanceled: autoReleaseWhenCanceled);\n        }\n\n        public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false, bool autoReleaseWhenCanceled = false)\n        {\n            if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<T>(cancellationToken);\n\n            if (!handle.IsValid())\n            {\n                throw new Exception(\"Attempting to use an invalid operation handle\");\n            }\n\n            if (handle.IsDone)\n            {\n                if (handle.Status == AsyncOperationStatus.Failed)\n                {\n                    return UniTask.FromException<T>(handle.OperationException);\n                }\n                return UniTask.FromResult(handle.Result);\n            }\n\n            return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, timing, progress, cancellationToken, cancelImmediately, autoReleaseWhenCanceled, out var token), token);\n        }\n\n        sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource<T>>\n        {\n            static TaskPool<AsyncOperationHandleConfiguredSource<T>> pool;\n            AsyncOperationHandleConfiguredSource<T> nextNode;","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/Cysharp/UniTask/blob/ceac8d6946b1125fe782cd171fbcb245b567dbf9/src/UniTask/Assets/Plugins/UniTask/Runtime/External/Addressables/AddressablesAsyncExtensions.cs#L270-L306","documentation":"Thrown by the ToUniTask/WithCancellation extension methods for Unity Addressables' AsyncOperationHandle<T>. Before wrapping the handle into a UniTask source, the code checks handle.IsValid(); if false the handle was already released, never initialized, or is a default handle. There is no valid operation to await so it throws immediately.","triggerScenarios":"Calling handle.ToUniTask() or handle.WithCancellation(token) after Addressables.Release(handle) was already called. Or passing a handle variable that was never assigned a valid load result (left at default). Or re-using a handle stored in a cache after it was released.","commonSituations":"Releasing an Addressables asset handle and then attempting to await it again in a different code path. A reference-counting bug where a handle is released prematurely. Storing handles in a dictionary and accessing after eviction/release.","solutions":["Do not call Addressables.Release(handle) until you are completely done awaiting the handle.","Check handle.IsValid() before calling ToUniTask/WithCancellation: if (!handle.IsValid()) return;","Use a reference-counted asset manager that tracks handle lifecycle and prevents premature release.","Ensure handles are loaded fresh per async operation rather than cached and reused after release."],"exampleFix":"// before\nAddressables.Release(handle);\nawait handle.WithCancellation(cancellationToken); // throws\n\n// after\nawait handle.WithCancellation(cancellationToken);\n// ... use handle.Result ...\nAddressables.Release(handle); // release only after all awaits are done","handlingStrategy":"validation","validationCode":"if (!handle.IsValid())\n{\n    // handle was released or never loaded; do not call ToUniTask\n    return;\n}\nvar result = await handle.ToUniTask();","typeGuard":"static bool IsValidHandle<T>(AsyncOperationHandle<T> handle) => handle.IsValid();","tryCatchPattern":"try\n{\n    var result = await handle.ToUniTask(cancellationToken: ct);\n}\ncatch (Exception ex) when (ex.Message.Contains(\"invalid operation handle\"))\n{\n    // Handle was released before await; reload the asset\n    handle = Addressables.LoadAssetAsync<T>(key);\n}","preventionTips":["Never call Addressables.Release(handle) before all awaits on that handle are complete.","Use a reference-counted handle manager that tracks active consumers.","Load handles fresh per operation rather than caching and reusing after release."],"tags":["addressables","unity","resource-management","async","handle"],"backgroundTag":null,"analyzedSha":"ceac8d6946b1125fe782cd171fbcb245b567dbf9","analyzedAt":"2026-08-13T19:16:39.025Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}