Quick answer: Don’t check is_action_just_pressed in both _process and _physics_process — pick one. For event-based handling, filter echoes with event.is_echo() and use _unhandled_input instead of _input.

The player presses Jump once and the character jumps twice or jumps to twice the height. You add a log, see two consecutive just_pressed calls in the same press, and wonder how Godot can detect two key downs from a single physical press. It can’t. The duplication is in your callback wiring.

Cause 1: Checked in Two Places

The most common cause: Input.is_action_just_pressed is checked in both _process and _physics_process in the same frame. The flag stays true for a full frame, so both callbacks see it as true and both jump.

# Anti-pattern: both callbacks consume the same press
func _process(delta):
    if Input.is_action_just_pressed("jump"):
        queue_jump_animation()

func _physics_process(delta):
    if Input.is_action_just_pressed("jump"):
        velocity.y = jump_force   # also fires this frame

If the press is registered between a physics tick and the following render frame, both callbacks observe the rising edge. The animation queues twice, or two physics frames apply jump_force back-to-back.

The fix is to read the press in one place and store a one-frame queued action:

var jump_queued: bool = false

func _process(delta):
    if Input.is_action_just_pressed("jump"):
        jump_queued = true

func _physics_process(delta):
    if jump_queued:
        velocity.y = jump_force
        queue_jump_animation()
        jump_queued = false

The press is captured once in _process (rising-edge accuracy) and consumed once in _physics_process (deterministic physics timing).

Cause 2: Unfiltered Key Echoes

If you handle keys in _input directly:

func _input(event):
    if event is InputEventKey and event.pressed and event.keycode == KEY_SPACE:
        jump()   # fires repeatedly while space is held

The OS sends key-repeat events while a key is held. The InputEventKey has is_echo() == true for those repeats. Filter them out:

func _input(event):
    if event is InputEventKey and event.pressed and not event.is_echo() and event.keycode == KEY_SPACE:
        jump()

Better: use the InputMap and check event.is_action_pressed("jump"), which has an optional allow_echo argument defaulting to false:

func _input(event):
    if event.is_action_pressed("jump"):   # ignores echoes by default
        jump()

Cause 3: Both _input and _unhandled_input Handling the Same Event

Godot dispatches input events to _input first, then to _unhandled_input if nothing called get_viewport().set_input_as_handled(). If you implement jump handling in both, a single key press fires both callbacks. Pick one:

# Use _unhandled_input for gameplay so UI takes priority
func _unhandled_input(event):
    if event.is_action_pressed("jump"):
        jump()
        get_viewport().set_input_as_handled()

Verifying

Add a print with frame number:

func _unhandled_input(event):
    if event.is_action_pressed("jump"):
        print("jump on frame ", Engine.get_process_frames())

One print per physical press means the fix is in. Two prints on the same frame — you’re still double-binding.

“Pick one callback. Filter one echo. Set input as handled. The whole class of input double-fire bugs disappears.”

Almost all “double-jump on one press” reports trace back to _process+_physics_process both checking is_action_just_pressed.