Quick answer: An action callback fires for started, performed, and canceled. Subscribe only to the phase you want (usually .performed), or branch on context.phase.

A jump handler runs two or three times per button press. The same method is subscribed to multiple action events — or it’s subscribed to the action itself, which fires every phase.

Subscribe to One Phase

jumpAction.performed += OnJump;   // fires once, when the action completes

void OnJump(InputAction.CallbackContext ctx)
{
    Jump();
}

.performed is the “it happened” phase for a button. .started fires on press-begin, .canceled on release — subscribing to all three runs your handler three times.

Or Branch on Phase

void OnJump(InputAction.CallbackContext ctx)
{
    if (ctx.phase != InputActionPhase.Performed) return;
    Jump();
}

If you must subscribe broadly, gate on ctx.phase at the top of the handler.

Press vs Hold vs Release

Map each to the right gameplay moment instead of treating them as duplicates.

Verifying

One button press = one jump. Charge mechanics use started/performed/canceled deliberately. No phantom double-inputs.

“Actions fire per phase. Subscribe to the one you mean, or check context.phase.”

Also unsubscribe in OnDisable — a handler still wired after the object is gone is another way callbacks ‘multiply’.