Quick answer: Tween created via create_tween() is owned by the scene tree of the calling node. If the node is freed mid-tween, the tween is killed and finished never emits. Infinite loops also never finish by design. For step-by-step callbacks use step_finished or insert tween_callback.

Here is how to fix Godot 4 Tween finished signals that never emit. Your tween animates correctly, the value reaches its target, but the connected finished handler never runs. Either the tween is being killed prematurely, or you have it configured for infinite loops, or you mistook step_finished for the right signal.

The Symptom

You call create_tween(), chain a few tween_property calls, connect tween.finished to a handler. The animation plays correctly. The handler never runs. Or it runs only sometimes, when the calling node happens to survive long enough.

What Causes This

Owner node freed. create_tween() returns a tween whose lifetime is tied to the calling node. When the owner is freed (scene change, queue_free), the tween is also freed and never emits finished.

Infinite loops. tween.set_loops(0) means infinite. The tween never finishes, so finished never fires.

Tween killed. If you call tween.kill() for any reason — or if pause_mode interactions kill it — finished is skipped.

Wrong signal name. step_finished fires per step in the chain. loop_finished fires per loop iteration. Only finished fires when the whole tween completes.

The Fix

Step 1: Connect to the right signal.

extends Node2D

func _ready():
    var tween = create_tween()
    tween.tween_property(self, "position", Vector2(200, 0), 1.0)
    tween.tween_property(self, "modulate:a", 0.0, 0.5)
    tween.finished.connect(_on_tween_done)

func _on_tween_done():
    print("Tween complete")
    queue_free()

Step 2: Use callbacks for mid-chain hooks.

func _ready():
    var tween = create_tween()
    tween.tween_property(self, "position:x", 200, 1.0)
    tween.tween_callback(_after_first)
    tween.tween_property(self, "position:y", 100, 1.0)

func _after_first():
    print("First leg done")

Step 3: Detect step completion.

var tween = create_tween()
tween.tween_property(self, "position", Vector2(100, 0), 1.0)
tween.tween_property(self, "position", Vector2(100, 100), 1.0)
tween.step_finished.connect(func(idx): print("Step ", idx, " done"))

Step 4: Survive scene transitions with explicit ownership. If the tween must outlive its node, create it on a long-lived node (autoload or HUD root):

# Autoload script TweenHost.gd
extends Node

func tween_for(target: Node) -> Tween:
    return create_tween()

# Calling code
var tween = TweenHost.tween_for(self)
tween.tween_property(self, "modulate:a", 0.0, 0.5)
tween.finished.connect(_on_done)

Step 5: Avoid infinite loops if you need finished. Use set_loops(N) with a finite N. Each iteration emits loop_finished(idx); after the Nth, finished fires.

Common Pitfalls

Connecting finished in _ready on a tween created in _ready works only if the node lives long enough for the tween to complete. For instances spawned and freed quickly (projectiles, hit effects), prefer await:

func shake_then_die():
    var tween = create_tween()
    tween.tween_property(self, "position:x", position.x + 10, 0.05)
    tween.tween_property(self, "position:x", position.x, 0.05)
    await tween.finished
    queue_free()

The await guarantees the next line runs after the tween completes. If the node is freed first, await never resumes, but you also do not need it to.

“Tween lifetime equals owner lifetime. Outlive the node and you outlive the tween. Use awaits or autoload hosts.”

Related Issues

For animation tree state issues, see AnimationTree State Machine. For signal disconnect issues, see Signal Lost After Scene Reload.

Connect finished. Use callbacks for steps. Await for short-lived nodes. The signal fires when it should.