Quick answer: The most common cause is transition conditions never being met. In GDScript state machines, the condition check may run before the state variable is updated, or transitions are split across _process() and _physics_process().

Here is how to fix Godot state machine stuck wrong state. Your character should transition from idle to run when they move, but they are stuck in idle forever. Or your AnimationTree state machine refuses to leave its starting state no matter what parameters you set. State machines in Godot 4 — whether hand-rolled in GDScript or built with AnimationTree — can silently refuse to transition for several fixable reasons.

The Symptom

Your state machine does not change states. In a custom GDScript implementation, the current state variable never updates even though your transition function is called. In an AnimationTree, travel() calls have no effect and the playback stays on its initial node. The game runs without errors but the character plays the wrong animation indefinitely.

What Causes This

1. Transition conditions never met. If you set velocity in _physics_process() but check it in _process(), the condition sees the previous frame’s value. This timing mismatch means the transition condition is never true at the moment it is checked.

2. States not connected in AnimationTree. The state machine editor requires explicit transitions between states. If two states are visually close but have no transition arrow, travel() cannot pathfind between them and silently fails.

3. Wrong condition evaluation order. In custom state machines with multiple if/elif checks, a higher-priority condition that is always true blocks lower-priority transitions. Checking is_on_floor() before jump input can make the jump state unreachable.

4. AnimationTree not active. The active property must be true for state transitions to process. It defaults to false in the editor, which catches many developers off guard.

The Fix

Keep all state logic in _physics_process() and evaluate transitions after movement. Check fall states before movement states so airborne characters do not enter ground states:

extends CharacterBody2D

enum State { IDLE, RUN, JUMP, FALL }
var current_state: State = State.IDLE

func _physics_process(delta):
  _update_velocity(delta)
  move_and_slide()
  var new_state = _get_next_state()
  if new_state != current_state:
    print("Transition: ", State.keys()[current_state],
        " -> ", State.keys()[new_state])
    current_state = new_state

func _get_next_state() -> State:
  match current_state:
    State.IDLE:
      if not is_on_floor(): return State.FALL
      if abs(velocity.x) > 10.0: return State.RUN
    State.RUN:
      if not is_on_floor(): return State.FALL
      if abs(velocity.x) < 10.0: return State.IDLE
    State.FALL:
      if is_on_floor(): return State.IDLE
  return current_state

For AnimationTree state machines, ensure the tree is active and use travel() with correctly connected states:

@onready var anim_tree = $AnimationTree
@onready var playback: AnimationNodeStateMachinePlayback = \
  anim_tree["parameters/playback"]

func _ready():
  anim_tree.active = true
  print("Current state: ", playback.get_current_node())

Related Issues

If your state machine transitions work but animations do not play, see AnimationPlayer not playing on scene load. If your state machine depends on overlap detection for transitions, check get_overlapping_bodies() returning empty to ensure physics queries are not giving stale data.

Put all state checks in _physics_process, after move_and_slide. Never split transitions across _process and _physics_process.