Quick answer: Tweens in Godot 4 are single-use. After they finish (or you call kill()), they refuse new tween_property calls. Call create_tween() fresh each time you want to animate.

The first time the player picks up a coin, the UI counter scales up nicely. The second pickup — nothing. The label sits at scale 1.0 like the tween never ran. You re-read the code, you log the function entry, you confirm tween_property is being called. The tween silently does nothing.

What Changed from Godot 3

In Godot 3, Tween was a node. You could call interpolate_property repeatedly across its lifetime and start() to play. In Godot 4, Tween is a transient object created by SceneTree.create_tween() (or Node.create_tween()). Once it finishes its chain of operations, it’s marked invalid. Subsequent tween_property calls produce an error in --verbose mode but no error in normal play — just silent no-ops.

The Fix: Fresh Tween Each Time

Replace your stored reference with a new one whenever you want to animate:

# WRONG: tween stored once, reused
var tween: Tween

func _ready():
    tween = create_tween()

func animate_pickup():
    tween.tween_property(label, "scale", Vector2.ONE * 1.5, 0.2)   # no-op after first finish

# RIGHT: new tween each call
var tween: Tween

func animate_pickup():
    if tween: tween.kill()
    tween = create_tween()
    tween.tween_property(label, "scale", Vector2.ONE * 1.5, 0.2)
    tween.tween_property(label, "scale", Vector2.ONE, 0.2)

The if tween: tween.kill() guard cancels any in-progress animation from a previous call before starting the new one. Without it, two rapid pickups would produce two competing tweens scaling the label simultaneously.

When You Want a Looping Tween

For an idle bob or breathing animation, create one tween and call set_loops():

func _ready():
    var tween = create_tween().set_loops().set_trans(Tween.TRANS_SINE)
    tween.tween_property(self, "position:y", position.y - 4, 0.6)
    tween.tween_property(self, "position:y", position.y, 0.6)

set_loops() with no argument loops forever. Pass an integer to loop a finite number of times. The tween stays alive until you explicitly kill() it or the node leaves the tree.

Parallel and Sequential Steps

By default each tween_property runs after the previous one. To run them in parallel, call set_parallel(true) on the tween or use parallel() on each step:

var tween = create_tween().set_parallel(true)
tween.tween_property(self, "position", target_pos, 0.3)
tween.tween_property(self, "modulate", Color.WHITE, 0.3)
# Both finish at the same time

Verifying

Run with --verbose in a terminal. After the first animation finishes, look for “Tween is invalid” warnings on subsequent calls — if you see them, you’re still reusing the reference. After the fix, no warnings appear and each call animates as expected.

“Tweens in Godot 4 are one-shot. Create a new one every time. The pattern is kill old, create new.”

Stale tween references are the single most common Godot 4 animation bug — fix it once and you’ll recognize it forever.