Quick answer: The most common cause is a missing Slot node in the Animation Blueprint. Even if you call PlayMontage successfully, the montage output must be routed through a Slot node (such as DefaultSlot) in your AnimGraph.

Here is how to fix Unreal animation montage not playing. You call PlayAnimMontage() and it returns a positive float value indicating the montage duration — but your character stands still in its idle pose. The montage is technically playing on the AnimInstance, but the visual result is nothing. This disconnect between a successful play call and zero visual output is confusing until you understand how Unreal’s montage pipeline actually routes animation data through the Animation Blueprint.

The Symptom

You have created an Animation Montage asset, assigned it the correct skeleton, and set up sections and notifies. In your character class or ability system, you call PlayAnimMontage(AttackMontage) and the return value is a positive number like 1.5, confirming the montage was accepted. But the character does not animate. It stays in whatever pose the state machine provides — usually idle.

If you pause the game and inspect the AnimInstance in the Animation Debugger, you might see that the montage is listed as active with a valid position and weight. The montage is genuinely playing internally, but its output never reaches the final pose that drives the skeletal mesh.

In another common variation, the montage plays the first frame and then immediately blends back to idle. This looks like a brief twitch or pop before the character returns to standing still. This happens when blend settings or montage priority is fighting with the base state machine.

What Causes This

1. No Slot node in the Animation Blueprint. This is the most common cause and the most confusing for developers new to Unreal’s animation system. Montages play in named slots (the default is DefaultSlot). The AnimGraph in your Animation Blueprint must contain a Slot node that matches the montage’s slot name. The Slot node blends the montage output on top of whatever the base state machine produces. Without this node, the montage has nowhere to output its pose.

2. Skeleton mismatch. If the montage was created for a different skeleton than the one your character’s mesh uses, PlayAnimMontage may still return a positive value in some engine versions but the AnimInstance will refuse to evaluate it. This produces no error in the Output Log — the montage simply does not affect the pose.

3. Play rate is zero or montage is immediately interrupted. Passing a play rate of 0.0 to PlayAnimMontage causes the montage to play at zero speed, effectively freezing on the first frame. If another montage or the state machine immediately overrides it, the visual result is no change. This also happens when Montage_Stop is called in the same frame as Montage_Play.

4. Blend settings suppress the montage. If the montage’s blend-in time is very long and its weight curve starts at zero, the montage will take a long time to become visible. If the montage is shorter than the blend-in time, it will finish before it ever reaches full weight, making it appear as though it never played.

The Fix

Step 1: Add a Slot node to your AnimGraph. Open your Animation Blueprint, go to the AnimGraph, and insert a Slot node between your state machine and the Output Pose. This is the most critical step.

// The AnimGraph should look like this conceptually:
// [State Machine] -> [Slot 'DefaultSlot'] -> [Output Pose]
//
// In the AnimBP event graph, the slot name must match
// the montage's Group/Slot setting (default: DefaultGroup.DefaultSlot)

// Verify the montage slot name in C++
void AMyCharacter::PlayAttack()
{
    if (AttackMontage)
    {
        UE_LOG(LogTemp, Warning, TEXT("Montage slot: %s"),
            *AttackMontage->GetGroupName().ToString());

        float Duration = PlayAnimMontage(AttackMontage, 1.0f);
        UE_LOG(LogTemp, Warning, TEXT("Montage duration: %f"), Duration);
    }
}

Step 2: Verify skeleton compatibility and AnimInstance setup. Ensure the montage, mesh, and AnimBP all use the same skeleton.

// Check skeleton compatibility at runtime
void AMyCharacter::BeginPlay()
{
    Super::BeginPlay();

    USkeletalMeshComponent* Mesh = GetMesh();
    UAnimInstance* AnimInst = Mesh->GetAnimInstance();

    if (!AnimInst)
    {
        UE_LOG(LogTemp, Error, TEXT("No AnimInstance! Check AnimBP assignment."));
        return;
    }

    UE_LOG(LogTemp, Warning, TEXT("AnimInstance class: %s"),
        *AnimInst->GetClass()->GetName());

    if (AttackMontage)
    {
        USkeleton* MontageSkel = AttackMontage->GetSkeleton();
        USkeleton* MeshSkel = Mesh->GetSkeletalMeshAsset()->GetSkeleton();

        if (MontageSkel != MeshSkel)
        {
            UE_LOG(LogTemp, Error,
                TEXT("Skeleton mismatch! Montage: %s, Mesh: %s"),
                *MontageSkel->GetName(), *MeshSkel->GetName());
        }
    }
}

Step 3: Use the AnimInstance directly for more control over playback. This approach gives you access to blend settings and callbacks.

// Play via AnimInstance for full control
UAnimInstance* AnimInst = GetMesh()->GetAnimInstance();
if (AnimInst && AttackMontage)
{
    float PlayRate = 1.0f;
    float StartPos = 0.f;
    bool bStopAll = true;

    AnimInst->Montage_Play(AttackMontage, PlayRate,
        EMontagePlayReturnType::MontageLength, StartPos, bStopAll);

    // Bind end delegate to know when montage finishes
    FOnMontageEnded EndDelegate;
    EndDelegate.BindUObject(this, &AMyCharacter::OnAttackMontageEnded);
    AnimInst->Montage_SetEndDelegate(EndDelegate, AttackMontage);
}

void AMyCharacter::OnAttackMontageEnded(UAnimMontage* Montage, bool bInterrupted)
{
    UE_LOG(LogTemp, Warning, TEXT("Montage ended. Interrupted: %s"),
        bInterrupted ? TEXT("Yes") : TEXT("No"));
}

Related Issues

If your montage plays but events that should fire from Anim Notifies are silent, check our guide on Blueprint events not firing for delegate binding issues. If montages work but Niagara effects that should spawn from Anim Notify States do not appear, see Niagara particles not spawning for component activation and bounds troubleshooting.

PlayMontage returning a duration means it accepted the request. The Slot node in AnimBP is what makes it visible.