Quick answer: The task’s Tick must eventually return EStateTreeRunStatus::Succeeded (or Failed). Cache the latest status in a member and return it from Tick when external events resolve the work.

An AI agent enters the “FollowTarget” state in your State Tree, walks toward the player, reaches them, and then keeps standing there. The task should have completed on arrival; the agent should transition to “Attack”. Instead, FollowTarget runs forever.

StateTree Task Lifecycle

Each task class overrides:

If Tick always returns Running, the state never advances. The StateTree won’t move to the next state until the current task completes (or a higher-priority transition condition fires).

The Fix

USTRUCT()
struct FFollowTargetTask : public FStateTreeTaskCommonBase
{
    GENERATED_BODY()

    EStateTreeRunStatus Status = EStateTreeRunStatus::Running;

    virtual EStateTreeRunStatus EnterState(FStateTreeExecutionContext& Ctx,
        const FStateTreeTransitionResult& Transition) const override
    {
        const_cast<FFollowTargetTask*>(this)->Status = EStateTreeRunStatus::Running;
        return EStateTreeRunStatus::Running;
    }

    virtual EStateTreeRunStatus Tick(FStateTreeExecutionContext& Ctx, const float DeltaTime) const override
    {
        auto* Agent = Ctx.GetInstanceData<FAgentData>(this);
        const float Dist = FVector::Dist(Agent->Pos, Agent->TargetPos);
        if (Dist < 100.0f) return EStateTreeRunStatus::Succeeded;
        return EStateTreeRunStatus::Running;
    }
};

Tick now checks the arrival condition and returns Succeeded when close enough. The StateTree dispatcher sees the completion, evaluates transitions, and moves to the next state (Attack).

External Completion via Delegates

For tasks waiting on an animation to finish or a network call to return:

EStateTreeRunStatus Tick(...) const override
{
    return CachedStatus;   // updated by delegate callback
}

// External delegate callback (set up in EnterState):
void OnAnimationFinished()
{
    CachedStatus = EStateTreeRunStatus::Succeeded;
}

EnterState binds the delegate; when the external event fires, it writes to CachedStatus. The next Tick reads it and returns the terminal status.

Transitions on Completion

Even with the task returning Succeeded, the state won’t advance without a transition configured. Open the State Tree asset:

  1. Select the FollowTarget state.
  2. Inspector → Transitions.
  3. Add a transition with Trigger = OnStateSucceeded, Target = Attack state.

Now Succeeded triggers the move to Attack. Without this transition, the task completes silently and the agent waits for some other transition condition to fire.

Diagnosing with the Debugger

Run with ai.debug.statetree.show 1. The debug overlay shows current state, task status, and which transitions are enabled. If the active task stays Running through your test case, it’s a task-completion bug; if it reaches Succeeded but doesn’t transition, it’s a transition-config bug.

Verifying

Spawn the agent. Watch the debug overlay during gameplay. The agent should progress FollowTarget → Attack within seconds of approaching the target. If it doesn’t, capture which state has Status = Running and inspect that task.

“StateTree tasks complete by returning a terminal status. Tasks plus transitions plus terminal returns — all three for the chain to advance.”

When migrating from Behavior Trees, the most common port-error is forgetting that StateTree tasks need explicit Succeeded returns — BTs auto-finished when their Execute returned.