Quick answer: In Godot 4, tween_method takes a Callable, not a string. Pass self.my_func or Callable(self, "my_func"). The callback signature must accept one parameter of the interpolated type — extra context requires bind().
Here is how to fix Godot tween_method not updating your property. You create a Tween, call tween_method, the tween runs, and your callback never fires — or the value stays frozen at the start. This is nearly always one of three things: Godot 3 string syntax ported to Godot 4, wrong parameter count on the method, or the tween finishing before your next frame because you forgot create_tween() returns a new instance each call.
The Symptom
You call tween_method expecting your setter function to be called every frame with the current interpolated value. Instead the property never changes, or the method fires once and then stops, or the editor prints Invalid Callable.
Variant: the tween duration elapses, finished fires, but the interpolation function was called zero times during the run.
What Causes This
Godot 3 string syntax. In Godot 3 you wrote tween.interpolate_method(self, "my_func", ...). In Godot 4 the method is tween_method and the first argument must be a Callable. Passing a string compiles (because GDScript coerces) but produces a broken callable that silently does nothing.
Wrong signature on the callback. The callback must accept exactly one argument matching the interpolated value type. If you declare func update(v: float, extra: String) without binding extras, the tween sees an arity mismatch and fails silently on some builds.
Tween reused after finishing. A Tween in Godot 4 is a one-shot object by default. Once its steps complete it enters a finished state and tween_method calls on the same instance are ignored. You must call create_tween() again to get a fresh one.
Node freed mid-tween. If the node that owns the Callable is freed or is no longer in the tree, the Callable becomes invalid and the tween stops firing without warning.
Using from with the wrong type. from(start_value) must match the declared destination type. Mixing Vector2 and Vector2i can make the tween skip the method step entirely.
The Fix
Step 1: Use Callable syntax.
extends Node2D
var _progress: float = 0.0
func _ready():
var tween = create_tween()
tween.tween_method(_set_progress, 0.0, 1.0, 2.0)
func _set_progress(value: float) -> void:
_progress = value
queue_redraw()
Pass the method reference directly: _set_progress, not "_set_progress". GDScript exposes bound methods as first-class Callables.
Step 2: Match the callback signature. The type you pass to tween_method (the start and end values) defines the parameter your callback receives. One parameter. No more.
# Interpolating a Color
tween.tween_method(_set_tint, Color.BLACK, Color.WHITE, 1.0)
func _set_tint(c: Color) -> void:
modulate = c
If you need extra context, use bind:
tween.tween_method(_set_tint.bind("hero"), Color.BLACK, Color.WHITE, 1.0)
func _set_tint(c: Color, label: String) -> void:
print(label, " now ", c)
modulate = c
Bound arguments are appended after the interpolated value. Order matters.
Step 3: Use from to override the start. By default tween_method uses the first argument as the start. If you want to begin from a specific value regardless, chain from():
tween.tween_method(_set_progress, 0.0, 1.0, 2.0) \
.from(0.25)
Step 4: Create a fresh tween for each run. A common mistake: storing the tween as a class member and replaying it.
# Wrong
var tween: Tween
func _ready():
tween = create_tween()
tween.tween_method(...)
func trigger():
tween.tween_method(...) # finished tween, ignored
# Right
func trigger():
var t = create_tween()
t.tween_method(...)
Typed Callable Syntax
For classes that override _ready in static typing contexts, declaring the Callable explicitly avoids inference surprises:
var cb: Callable = Callable(self, "_set_progress")
tween.tween_method(cb, 0.0, 1.0, 2.0)
This is equivalent to self._set_progress but makes the intent explicit and survives refactors where the method is moved.
step_finished for Chaining
If you need to run logic between tween steps, connect to step_finished:
tween.step_finished.connect(func(idx): print("step ", idx, " done"))
tween.tween_method(_set_progress, 0.0, 0.5, 1.0)
tween.tween_method(_set_progress, 0.5, 1.0, 1.0)
step_finished(idx) fires after each chained step; finished fires once after the whole tween completes.
“Callable, one parameter, fresh tween. The three rules of tween_method in Godot 4.”
Related Issues
For signal connection patterns, see Await Signal Never Completing. For general Godot 3 to 4 migration pain, Area2D Not Detecting StaticBody2D covers a similar naming shift.
Pass Callable, one parameter, create_tween() each time. Tween updates every frame.