# 5. Async Pitfalls
Part of [[Async Code]]. Previous: [[4. Worked Examples - Scenes Delays and Loads]].
> [!note] Verification status
> Pitfalls are taken from the official Awaitable manual (pooling, continuation rules, background work surviving Play Mode exit) and from the `WaitForSecondsAsync` cancellation note. **Code not compiled in Unity.**
## 5.1 Pitfalls
1. **Awaiting the same `Awaitable` instance twice.** They are pooled and this is officially unsafe. Call the helper again.
2. **Forgetting cancellation.** An `async Awaitable` method does not automatically die with the `MonoBehaviour`. Pass `destroyCancellationToken` and, for background or long-lived work, `Application.exitCancellationToken`.
3. **Touching Unity objects on a background thread.** Hop with `MainThreadAsync` first, or return data and let the main-thread caller apply it.
4. **`WaitForSecondsAsync` from a background thread.** The API is main-thread only.
5. **Using cancel to mean success** and then wondering why `ApplyHazard()` never ran. Catch `OperationCanceledException` deliberately, or don't use a token for that path.
6. **`async void` with a swallowed exception.** Prefer `async Awaitable` and either await it or use `LogExceptionsAndForget()` (official API on `Awaitable`). If you roll your own fire-and-forget helper, log non-cancel exceptions.
7. **`async Awaitable Start()` that throws after a wait, with nobody listening.** Same as above — make sure failures show up in the Console.
8. **Leaving Play Mode while a background compute is running.** Officially not stopped for you. Token it, or you will log errors from a domain that is already tearing down.
9. **Treating `Task` and `Awaitable` as interchangeable types.** You can `await` a `Task` from an `async Awaitable` method (the BootLoader-style mix is in the Scripting API example). You cannot assign one to the other. Tests should return `Task`, not `Awaitable` ([[3. UnityTest Async Tests and Domain Reloads]]).
10. **Assuming Addressables 2.9 has `ToAwaitable()`.** It does not. Use `handle.Task` or a coroutine, or upgrade to 4.x ([[3. Loading Instantiating and Releasing]]).
11. **Holding a huge object graph across an `await` on a background method** and accidentally using it after destroy. After every await, the world may have changed; check `this == null` (Unity's fake-null) or the token.
12. **Rewriting a tight per-frame loop as async waits "for style."** `Update` is still the right place for per-frame steering. Await is for *gaps* (I/O, time, scene loads, user prompts).
13. **Awaiting in `OnDestroy`.** Destruction is already happening; start no new async work there. Cancel work in `OnDisable` / `OnDestroy` instead.
14. **Comparing coroutine lifetime to async lifetime in a lecture without a demo.** Show a destroyed object with a missing token once. Students remember the exception.
## 5.2 A default template
```csharp
using System;
using System.Threading;
using UnityEngine;
public sealed class AsyncTemplate : MonoBehaviour
{
public async Awaitable RunAsync()
{
using var linked = CancellationTokenSource.CreateLinkedTokenSource(
destroyCancellationToken,
Application.exitCancellationToken);
try
{
await Awaitable.NextFrameAsync(linked.Token);
// work
}
catch (OperationCanceledException)
{
}
}
}
```
Start from that, then delete the parts you genuinely do not need.
## Official docs
- Awaitable manual: https://docs.unity.com/en-us/engine/6000.5/manual/scripting/optimization/async-await-support
- Continuations: https://docs.unity.com/en-us/engine/6000.5/manual/scripting/optimization/async-await-support/async-awaitable-continuations
- Cancel / WaitForSecondsAsync: https://docs.unity3d.com/6000.5/Documentation/ScriptReference/Awaitable.Cancel.html