Quick answer: Use a CancellationTokenSource per node, cancel in _ExitTree, pass the token to async methods, and gate post-await access with IsInsideTree.
A C# enemy AI awaits a tween, then sets the next move. Player kills the enemy mid-await, queue_free fires, scene reloads. The await resumes against a disposed node — ObjectDisposedException at runtime.
Cancellation Token Pattern
public partial class Enemy : CharacterBody3D
{
private CancellationTokenSource _cts = new();
public override async void _Ready()
{
try
{
await RunAi(_cts.Token);
}
catch (OperationCanceledException) { // expected on exit }
}
public override void _ExitTree()
{
_cts.Cancel();
_cts.Dispose();
}
}
Single source of cancellation. ExitTree flips the flag; all in-flight awaits throw at next opportunity.
Await Loops with Token Checks
private async Task RunAi(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
ct.ThrowIfCancellationRequested();
await ToSignal(GetTree().CreateTimer(1.0), "timeout");
if (!IsInsideTree()) return;
PickNextTarget();
}
}
Three layers of defense: token check, IsInsideTree check, then act. Any path can early-return safely.
Avoid Fire-and-Forget
async void methods (like _Ready) silently swallow exceptions. Don’t kick off side tasks with async void; use async Task and store the task so you can await/cancel it.
Timer Await Caveat
SceneTreeTimer signals don’t propagate cancellation. Use Task.Delay(ms, ct) with a token for cancellable waits, or wrap ToSignal in a try/catch that respects the token:
await Task.WhenAny(
ToSignal(GetTree().CreateTimer(1), "timeout").AsTask(),
Task.Delay(Timeout.Infinite, ct)
);
ct.ThrowIfCancellationRequested();
Verifying
Kill enemies mid-action repeatedly. No ObjectDisposedException. Debug log shows clean cancellation messages. Scene reload doesn’t leak in-flight tasks (check Task count via diagnostic profiler).
“Async + scene tree = a contract. Make ExitTree the contract’s end.”
For multiplayer or save systems, cancellation tokens also let you abort long operations on disconnect/save-cancel. One pattern, many uses.