Quick answer: A Tween created with create_tween() is auto-killed when its bound node leaves the tree. Bind it to a node that will live long enough, or store the tween in a member variable so it doesn’t go out of scope before the callback fires.

You write tween.tween_callback(self.spawn_loot), the animation finishes, and spawn_loot never runs. Or await tween.finished hangs forever. The pattern is the same: the tween was killed before it reached the callback step.

The Symptom

Tween animation visibly plays. The callback at the end does not fire. Or an await tween.finished never resumes. No error, no warning.

What Causes This

In Godot 4, create_tween() on a Node binds the new Tween to that node. If the node leaves the tree (queue_free, scene change, the parent free) before the tween finishes, the engine kills the tween. Killed tweens never emit finished and never run pending callbacks.

It is also possible to lose the reference. If you create a Tween, do not bind it to a node, and store it in a local variable that goes out of scope, the engine garbage-collects it.

The Fix

Pattern 1: Bind to a stable node. The most common: parent node lives at least until the tween finishes.

extends Node2D

func show_damage(amount: int) -> void:
    var label := DamageLabel.instantiate()
    add_child(label)
    label.text = str(amount)

    var tween := create_tween()           # bound to self
    tween.tween_property(label, "position:y", label.position.y - 40, 0.6)
    tween.tween_callback(label.queue_free)

Pattern 2: Bind to the animated child, not the parent. If the animated node is short-lived but you want to await its completion, bind the tween to it but be aware that freeing the child kills the tween.

Pattern 3: Awaited callback.

func animate_and_continue() -> void:
    var tween := create_tween()
    tween.tween_property(self, "modulate:a", 0.0, 0.5)
    await tween.finished
    queue_free()

This works as long as self is still in the tree when the tween finishes. If queue_free earlier in the chain happens, the await hangs.

Verifying

Connect to tween.finished and add a print statement; also print at the top of your callback. If neither prints, the tween was killed. Use tween.is_valid() right before await — it returns false if the tween was killed.

One-Shot Pattern Done Safely

func flash() -> void:
    var t := create_tween().set_parallel(true)
    t.tween_property(self, "modulate", Color.WHITE, 0.1)
    t.tween_property(self, "scale", Vector2(1.2, 1.2), 0.1)
    t.chain().tween_property(self, "scale", Vector2.ONE, 0.1)
    t.tween_callback(func():
        if is_inside_tree():
            on_flash_done()
    )

Wrapping the callback body in is_inside_tree() avoids errors if the node was freed during the animation.

“Bind to a node that lives long enough. Check is_valid before await. Wrap callbacks with is_inside_tree.”

Related Issues

For await never resuming, see await never resumes. For queue_free timing, see queue_free already freed.

Bind to a stable node. Validate before await. Callbacks fire.