Quick answer: Stop Action fires only when IsAlive() returns false. Looping systems are always alive, so the action never triggers. Set Looping off, or call ps.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear) to force completion. Also confirm sub-emitters all finish — the parent waits on them.

Here is how to fix Unity ParticleSystems whose Stop Action Disable never disables the GameObject. You set the Stop Action to Disable so the system goes inactive after one shot. The particles play, fade out, and the object stays active forever, sitting around as a leak in your pool. The Stop Action depends on IsAlive() reporting false, and looping or sub-emitters can keep that from happening.

The Symptom

A ParticleSystem with Stop Action set to Disable (or Destroy or Callback) plays correctly but never triggers the action. The GameObject stays active. Spawning many of these leaks objects into the scene.

What Causes This

Looping is enabled. A looping system has indefinite life. IsAlive() returns true forever. Stop Action fires only on transition to dead.

Sub-emitter still alive. The parent system considers itself alive while any child sub-emitter has live particles. A long-lifetime sub-emitter keeps the whole tree alive.

Stop never called. Stop Action triggers when the system stops emitting and the last particle dies. If you never call Stop() and Looping is on, the system never stops.

Wrong Stop behavior. Calling Stop() with the default StopEmitting waits for existing particles to complete. With long-lifetime particles this delays the disable significantly.

The Fix

Step 1: Disable Looping for one-shot effects. In the main module of the ParticleSystem inspector, uncheck Looping. Also set Duration to the longest particle lifetime so the burst plays out before stopping.

Step 2: Enable Stop Action correctly. Bottom of the main module, set Stop Action to Disable. The system disables its GameObject when alive count hits zero.

Step 3: Force stop when needed.

using UnityEngine;

[RequireComponent(typeof(ParticleSystem))]
public class VfxBurst : MonoBehaviour
{
    private ParticleSystem ps;

    void Awake() { ps = GetComponent<ParticleSystem>(); }

    public void Burst()
    {
        gameObject.SetActive(true);
        ps.Play(true);  // includes children sub-emitters
    }

    public void CancelImmediately()
    {
        ps.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
    }
}

Step 4: Make sub-emitters non-looping too. Open each sub-emitter ParticleSystem and uncheck Looping. Set their lifetime to a value smaller than or equal to the parent’s emission window.

Step 5: For pooled effects, do not rely on Stop Action. If you pool VFX, disabling them prevents reuse. Instead, listen for completion explicitly and return to the pool:

void Update()
{
    if (ps.IsAlive(true) == false)
    {
        pool.Return(gameObject);
    }
}

Skip Stop Action entirely. The pool decides when to disable.

Verifying In Inspector

During Play, watch the ParticleSystem inspector header. The Live Particles count tells you how many particles are still alive. Once it reaches zero and Playing is false, the Stop Action should fire on the next frame. If the count stays above zero, your sub-emitters or longer-lifetime particles are keeping the system alive.

Common Mistakes

Setting Stop Action on the parent but leaving sub-emitters with Looping on. The sub-emitter dictates aliveness; the parent obeys.

Calling SetActive(false) on the parent immediately after Play. The ParticleSystem cannot fire Stop Action on a disabled GameObject. Either let the action fire naturally, or fully manage the lifecycle yourself.

“Stop Action waits for IsAlive false. Looping systems are always alive. The two facts together explain 90% of stuck particle effects.”

Related Issues

For particles failing to play, see Particle System Not Playing. For sub-emitter loops, see Sub-Emitter Infinite Loop.

Looping off on parent and children. Stop with StopEmittingAndClear when you need it now. Disable comes for free.