Quick answer: Use create_tween() on a node that stays alive for the tween’s duration, or use get_tree().create_tween() for tree-bound lifetime. await tween.finished after kicking off.

You write a fade-out then queue_free. The fade plays. The signal never fires because by the time the tween emits, the bound node is already freeing.

The Symptom

Code after await tween.finished never runs. Or signal handler isn’t called.

The Fix

# BAD: bound to self, then free self mid-tween
var tw = create_tween()
tw.tween_property(self, "modulate:a", 0.0, 0.5)
await tw.finished     # may not return if self frees first
queue_free()

# GOOD: tree-bound, free after await
var tw = get_tree().create_tween()
tw.tween_property(self, "modulate:a", 0.0, 0.5)
await tw.finished
queue_free()

Tree-bound tweens survive their target’s removal. They’ll fire finished on schedule even if the property target frees, then bow out cleanly.

Killed Tweens

If you call tw.kill(), finished still emits but tw.is_valid() returns false. Check before reading state.

Verifying

Print before queue_free, after await: both lines run. Drop tween-finishing logic into the await; it executes in order.

“Tree-bound tween. Await emits. Free after.”

Related Issues

For Camera2D zoom smoothing, see zoom jitter. For Resource UID conflicts, see UID conflict.

Tree-bound. Awaits land. Cleanup runs.