Quick answer: Set Stop Action = Callback in the Main module. Implement OnParticleSystemStopped on a script on the root and call your pool’s Release there. Always Stop with true for stopChildren so sub-emitters cancel too.

VFX pool grows. Each one-shot effect is supposed to return after playing, but stays in the live pool forever. Sub-emitters keep the parent IsAlive true; your “wait until done” never resolves.

The Symptom

Pool count climbs over time. Profiler shows ParticleSystem instances accumulating. ParticleSystem.IsAlive() never returns false even after the visible effect finishes.

The Fix

Step 1: Stop Action = Callback.

Particle System → Main module:
  Stop Action: Callback

Unity will call OnParticleSystemStopped on every MonoBehaviour on the same GameObject when the system and all subs finish.

Step 2: Receive the callback.

public class PooledVFX : MonoBehaviour
{
    public System.Action<PooledVFX> onDone;

    void OnParticleSystemStopped()
    {
        onDone?.Invoke(this);
    }
}

Pool subscribes onDone to release. Reset Stop Action to Callback once on prefab; the callback fires for each play.

Step 3: Force-stop on demand.

vfx.Stop(withChildren: true, ParticleSystemStopBehavior.StopEmittingAndClear);

StopEmittingAndClear cancels all live particles immediately; OnParticleSystemStopped fires same frame. Use for early returns (player despawned, scene ending).

Verifying

Spawn-and-forget many VFX. Wait for them to finish. Pool count stabilizes. Without the callback, pool count grows linearly.

“Stop Action Callback. OnParticleSystemStopped releases. Sub-emitters covered.”

Related Issues

For sub-emitter inheritance, see sub-emitter color. For trail leaks, see trail leak.

Callback fires. Pool releases.