Quick answer: The most common cause is that the AI controller is not running the behavior tree. Verify that RunBehaviorTree is called on the AI controller after the pawn is possessed.

Here is how to fix Unreal behavior tree task not executing. You have built a behavior tree in Unreal Engine, added tasks for your AI to patrol, chase, and attack, connected everything with sequences and selectors — and the AI just stands there doing nothing. The behavior tree editor shows no active node, your custom BTTask is never called, and the AI pawn is completely inert. Behavior trees have several setup requirements that are easy to miss, and a single misconfiguration can silently prevent the entire tree from running.

The Symptom

Your AI-controlled pawn does not execute any behavior tree tasks. The pawn spawns into the world but remains stationary. Opening the behavior tree editor while selecting the AI pawn shows no highlighted nodes, indicating the tree is not being traversed. Custom UBTTaskNode subclasses have their ExecuteTask function set with breakpoints or log statements that never trigger.

In some cases, the tree starts running but gets stuck on a specific branch. A selector node always takes its first child and never evaluates alternatives. A sequence node executes the first task then stops. Or the tree runs once and then becomes inactive, never re-evaluating from the root. These variants point to different underlying causes but share the common symptom of tasks not executing when expected.

What Causes This

1. The AI controller is not running the behavior tree. The behavior tree does not run automatically when assigned to an AI controller. You must explicitly call RunBehaviorTree on the controller, and this must happen after the pawn is possessed. If you call it in the controller's constructor or before possession, the pawn reference is null and the tree cannot execute.

2. Blackboard keys are not set, causing decorators to block execution. Decorator nodes on branches check blackboard values as conditions. If a required blackboard key has never been set (its value is Not Set), a Blackboard Decorator checking IsSet will fail, blocking the entire branch. This is the most common reason a specific branch never executes even though the tree is running.

3. The BTTask returns the wrong result type. If your custom task's ExecuteTask returns EBTNodeResult::Succeeded immediately, the tree moves on in the same frame. If it returns Failed, the parent composite aborts that branch. If you intended a latent task (one that takes time) but forgot to return InProgress, the task completes instantly without doing any work. Conversely, if you return InProgress but never call FinishLatentTask, the tree hangs on that node forever.

4. The AI controller class is not assigned to the pawn. The pawn must have its AIControllerClass set to your custom AI controller (or at least AAIController), and AutoPossessAI must be set to an appropriate value like PlacedInWorldOrSpawned. If the pawn has no AI controller, there is nothing to run the behavior tree.

The Fix

Step 1: Set up the AI controller to run the behavior tree on possession. The correct place to start the behavior tree is in the controller's OnPossess function, which is called when the controller takes ownership of a pawn:

// MyAIController.h
#pragma once
#include "CoreMinimal.h"
#include "AIController.h"
#include "MyAIController.generated.h"

UCLASS()
class AMyAIController : public AAIController
{
    GENERATED_BODY()

public:
    AMyAIController();

protected:
    virtual void OnPossess(APawn* InPawn) override;
    virtual void OnUnPossess() override;

    UPROPERTY(EditDefaultsOnly, Category = "AI")
    UBehaviorTree* BehaviorTreeAsset;

    UPROPERTY(EditDefaultsOnly, Category = "AI")
    UBlackboardData* BlackboardAsset;
};
// MyAIController.cpp
#include "MyAIController.h"
#include "BehaviorTree/BehaviorTree.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "BehaviorTree/BehaviorTreeComponent.h"

AMyAIController::AMyAIController()
{
    // Ensure the controller has BT and BB components
    BehaviorTreeComponent = CreateDefaultSubobject
        <UBehaviorTreeComponent>(TEXT("BehaviorTree"));
    Blackboard = CreateDefaultSubobject
        <UBlackboardComponent>(TEXT("Blackboard"));
}

void AMyAIController::OnPossess(APawn* InPawn)
{
    Super::OnPossess(InPawn);

    if (!BehaviorTreeAsset)
    {
        UE_LOG(LogTemp, Error,
            TEXT("AI: No BehaviorTree asset assigned"));
        return;
    }

    // Initialize the blackboard
    UBlackboardData* BBAsset = BlackboardAsset
        ? BlackboardAsset
        : BehaviorTreeAsset->BlackboardAsset;

    if (BBAsset && UseBlackboard(BBAsset, Blackboard))
    {
        UE_LOG(LogTemp, Log,
            TEXT("AI: Blackboard initialized"));
    }
    else
    {
        UE_LOG(LogTemp, Error,
            TEXT("AI: Failed to init blackboard"));
    }

    // Start the behavior tree
    if (!RunBehaviorTree(BehaviorTreeAsset))
    {
        UE_LOG(LogTemp, Error,
            TEXT("AI: RunBehaviorTree failed for %s"),
            *BehaviorTreeAsset->GetName());
    }
    else
    {
        UE_LOG(LogTemp, Log,
            TEXT("AI: BT running on %s"),
            *InPawn->GetName());
    }
}

void AMyAIController::OnUnPossess()
{
    // Stop the BT when the pawn is released
    if (BehaviorTreeComponent)
    {
        BehaviorTreeComponent->StopTree();
    }
    Super::OnUnPossess();
}

The key points: RunBehaviorTree is called inside OnPossess, not in the constructor or BeginPlay. The blackboard must be initialized before the tree starts via UseBlackboard. Both the BehaviorTreeComponent and BlackboardComponent are created as default subobjects so they exist when the controller is spawned.

Step 2: Create a custom BTTask that properly handles latent execution. Most useful AI tasks take time to complete. Here is a pattern for a custom move-to-location task with correct latent execution:

// BTTask_MoveToTarget.h
#pragma once
#include "CoreMinimal.h"
#include "BehaviorTree/BTTaskNode.h"
#include "BTTask_MoveToTarget.generated.h"

UCLASS()
class UBTTask_MoveToTarget : public UBTTaskNode
{
    GENERATED_BODY()

public:
    UBTTask_MoveToTarget();

    virtual EBTNodeResult::Type ExecuteTask(
        UBehaviorTreeComponent& OwnerComp,
        uint8* NodeMemory) override;

    virtual void TickTask(
        UBehaviorTreeComponent& OwnerComp,
        uint8* NodeMemory, float DeltaSeconds) override;

    virtual EBTNodeResult::Type AbortTask(
        UBehaviorTreeComponent& OwnerComp,
        uint8* NodeMemory) override;

protected:
    UPROPERTY(EditAnywhere, Category = "Blackboard")
    FBlackboardKeySelector TargetLocationKey;

    UPROPERTY(EditAnywhere, Category = "Task")
    float AcceptableRadius = 100.0f;
};
// BTTask_MoveToTarget.cpp
#include "BTTask_MoveToTarget.h"
#include "AIController.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "Navigation/PathFollowingComponent.h"

UBTTask_MoveToTarget::UBTTask_MoveToTarget()
{
    NodeName = "Move To Target Location";
    // Enable TickTask for monitoring movement
    bNotifyTick = true;
    // Allow this task to be aborted
    bNotifyTaskFinished = true;
}

EBTNodeResult::Type UBTTask_MoveToTarget::ExecuteTask(
    UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
    AAIController* AIC = OwnerComp.GetAIOwner();
    if (!AIC)
    {
        UE_LOG(LogTemp, Error,
            TEXT("BTTask: No AI controller"));
        return EBTNodeResult::Failed;
    }

    UBlackboardComponent* BB = OwnerComp.GetBlackboardComponent();
    if (!BB)
    {
        UE_LOG(LogTemp, Error,
            TEXT("BTTask: No blackboard"));
        return EBTNodeResult::Failed;
    }

    FVector TargetLoc = BB->GetValueAsVector(
        TargetLocationKey.SelectedKeyName);

    EPathFollowingRequestResult::Type Result =
        AIC->MoveToLocation(TargetLoc, AcceptableRadius);

    if (Result == EPathFollowingRequestResult::AlreadyAtGoal)
    {
        return EBTNodeResult::Succeeded;
    }

    if (Result == EPathFollowingRequestResult::Failed)
    {
        UE_LOG(LogTemp, Warning,
            TEXT("BTTask: MoveToLocation failed"));
        return EBTNodeResult::Failed;
    }

    // Movement is in progress - return InProgress
    // and wait for TickTask to detect completion
    return EBTNodeResult::InProgress;
}

void UBTTask_MoveToTarget::TickTask(
    UBehaviorTreeComponent& OwnerComp,
    uint8* NodeMemory, float DeltaSeconds)
{
    AAIController* AIC = OwnerComp.GetAIOwner();
    if (!AIC)
    {
        FinishLatentTask(OwnerComp, EBTNodeResult::Failed);
        return;
    }

    EPathFollowingStatus::Type Status =
        AIC->GetMoveStatus();

    if (Status == EPathFollowingStatus::Idle)
    {
        // Movement finished (reached goal or stopped)
        FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded);
    }
}

EBTNodeResult::Type UBTTask_MoveToTarget::AbortTask(
    UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
    AAIController* AIC = OwnerComp.GetAIOwner();
    if (AIC)
    {
        AIC->StopMovement();
    }
    return EBTNodeResult::Aborted;
}

The critical pattern is returning EBTNodeResult::InProgress from ExecuteTask and later calling FinishLatentTask when the work is done. Without this, the tree either skips the task instantly or hangs forever. The AbortTask override ensures clean cancellation when a higher-priority branch activates.

Step 3: Verify blackboard key setup and decorator conditions. Check that every blackboard key referenced in your decorators is actually being set somewhere. A Blackboard Decorator set to Is Set on a key that was never written to will always fail, blocking the entire branch silently. Add logging to confirm keys are being set correctly at runtime.

Related Issues

If the behavior tree runs initially but stops after a pawn death and respawn, the AI controller may have been destroyed and recreated without re-running the behavior tree. Ensure OnPossess starts the tree for the new pawn. If the tree runs but the AI pawn does not move, verify that a NavMesh covers the area. MoveToLocation requires navigation data — without a NavMesh volume, all pathfinding requests fail silently.

If decorators seem to evaluate incorrectly, check the Observer Aborts setting on the decorator. None means the decorator is only checked when the branch is entered. Self means the decorator can abort its own branch if the condition changes. Lower Priority means it can also abort lower-priority sibling branches. Incorrect abort settings are a common cause of behavior trees not reacting to blackboard changes in real time.

If RunBehaviorTree is called before OnPossess, the pawn is null and nothing runs. Always start the BT inside OnPossess.