Quick answer: Output Event handlers must subclass VFXOutputEventAbstractHandler, sit on the same GameObject as the VisualEffect, and have eventName matching the graph’s Output Event node exactly. The graph node must be wired to fire on real conditions (spawn, die, custom).

Here is how to fix Unity VFX Graph Output Events that never reach your C# handler. The graph fires the event, particles spawn correctly, but the C# callback you wrote never runs. Output Events bridge GPU effect simulation back to CPU game logic; the wiring is strict and easy to miss in two places: the handler script and the graph node itself.

The Symptom

VFX Graph effect plays correctly. You added an Output Event node fed from particle die, expecting the C# handler to spawn pickup objects. The handler never receives events. Logs are silent. The Visual Effect graph runs fine.

What Causes This

Handler not on the right GameObject. The handler component must sit alongside the VisualEffect component on the same GameObject. Placement on a parent or child is silently ignored.

Event name mismatch. The Output Event node has an Event Name string. The handler has an eventName field. They must be identical, including casing.

Output Event not fed. The node only fires when its Spawn or Die input is connected to actual particle events. A disconnected node does nothing.

VFX culled. If the VisualEffect is offscreen and culled, both simulation and event firing stop. CullingMode determines this.

The Fix

Step 1: Write a handler subclass.

using UnityEngine;
using UnityEngine.VFX;
using UnityEngine.VFX.Utility;

[RequireComponent(typeof(VisualEffect))]
public class PickupSpawnHandler : VFXOutputEventAbstractHandler
{
    [SerializeField] private GameObject pickupPrefab;

    public override bool canExecuteInEditor => true;

    public override void OnVFXOutputEvent(VFXEventAttribute attr)
    {
        Vector3 pos = attr.GetVector3("position");
        Instantiate(pickupPrefab, pos, Quaternion.identity);
    }
}

Step 2: Match event names exactly.

// In the VFX graph Output Event node:
// Event Name = OnPickupDrop

// In the handler component inspector:
// Event Name = OnPickupDrop  (must match)

Step 3: Wire the Output Event in the graph. Open the VFX graph. Drag the Spawn/Die output of a context (e.g., Initialize Particle → Update → OnDie) into the Output Event node. Without this connection, the node never fires.

Step 4: Set culling mode for testing.

[SerializeField] private VisualEffect vfx;

void Awake()
{
    vfx.cullingMode = VFXCullingFlags.CullNone;  // always simulate
}

Use only while debugging. Production should keep default culling for performance.

Step 5: Verify with logs.

public override void OnVFXOutputEvent(VFXEventAttribute attr)
{
    Debug.Log($"VFX event: pos={attr.GetVector3("position")}");
}

If the log appears, your handler is wired up correctly. If not, the chain has a break.

Reading Custom Attributes

Output events carry a small attribute set: position, velocity, color, lifetime, particleId. To pass game-specific data, store values in the particle’s built-in attributes (e.g., color as RGBA where you encode item ID in alpha):

int itemId = Mathf.RoundToInt(attr.GetVector4("color").w * 255);

Hacky but works for limited per-particle metadata until Unity allows custom attributes on output events.

“Same GameObject, same name, wired-up node, awake handler. Four checks for events to flow.”

Related Issues

For VFX particles not spawning, see VFX Graph Particles Not Spawning. For event firing in particle systems, see Particle Collision Message.

Subclass handler. Match name. Wire the node. CullNone for debug. Events arrive.