Quick answer: If you call is_action_just_pressed() in _process(), it can return true for multiple frames because _process() may run more than once before the input system resets the 'just pressed' state. The input polling state is synced with the physics tick, not the rendering frame rate.
Here is how to fix Godot input action just pressed multiple times. You press the jump button once. Your character jumps twice — or fires two bullets, or opens the inventory and immediately closes it. The culprit is almost always a mismatch between where you check Input.is_action_just_pressed() and how Godot's frame timing actually works. This is a subtle bug that affects nearly every Godot developer at some point.
The Symptom
You have an action mapped in your Input Map — say, "jump" or "shoot". You call Input.is_action_just_pressed("jump") in your _process() function, expecting it to return true for exactly one frame when the player presses the key. Instead, it returns true for two or sometimes three frames in a row. The result is that your one-shot action fires multiple times per key press.
This is especially noticeable at high frame rates. At 60 FPS the bug might be rare, but at 144 FPS or higher, _process() runs multiple times between physics ticks, and each call sees the same "just pressed" state. The action appears to double-fire unpredictably, making the game feel broken.
What Causes This
The root cause is a timing mismatch between rendering frames and physics frames. In Godot 4, the input polling state — what is_action_just_pressed() and is_action_just_released() return — is updated once per physics tick, not once per rendering frame.
_process(delta) runs once per rendering frame, which can be 60, 144, or even 300+ times per second depending on the display and whether V-Sync is enabled. _physics_process(delta) runs at a fixed rate, defaulting to 60 times per second. Between two physics ticks, _process() may execute two or more times, and during all of those calls, is_action_just_pressed() will return true because the input state has not been reset yet.
Here is what the broken code looks like:
# Broken: _process runs multiple times per physics tick
extends CharacterBody2D
func _process(delta):
if Input.is_action_just_pressed("shoot"):
fire_bullet() # May fire 2-3 bullets per press!
The Fix
There are two reliable solutions depending on your use case.
Option 1: Move the check to _physics_process() — since the input state resets once per physics tick, checking in _physics_process() guarantees the action fires exactly once:
# Fixed: check in _physics_process instead
extends CharacterBody2D
func _physics_process(delta):
if Input.is_action_just_pressed("shoot"):
fire_bullet() # Fires exactly once per press
Option 2: Use _unhandled_input() — this is the preferred approach for one-shot actions because it is event-driven rather than polling-based. The callback fires exactly once per input event:
# Best: event-driven input handling
extends CharacterBody2D
func _unhandled_input(event):
if event.is_action_pressed("shoot"):
fire_bullet()
if event.is_action_pressed("jump"):
try_jump()
Notice the method is event.is_action_pressed(), not Input.is_action_just_pressed(). The event parameter is an InputEvent object that represents a single input occurrence. It fires once when the key goes down, making double-triggering impossible.
Option 3: Consume the event — if you have multiple nodes that might react to the same input, call set_input_as_handled() to stop propagation:
# Consume the event to prevent other nodes from reacting
func _unhandled_input(event):
if event.is_action_pressed("shoot"):
fire_bullet()
get_viewport().set_input_as_handled()
This is particularly important if you have an autoload and a player node both listening for the same action. Without consuming the event, both nodes process it independently.
Why This Works
_unhandled_input() is event-driven. Godot calls it exactly once per discrete input event — one call for key down, one call for key up. There is no frame-rate dependency because the callback is not tied to the rendering loop or the physics loop. It fires in response to actual hardware events.
_physics_process() works because the "just pressed" state is designed to be valid for exactly one physics tick. Since _physics_process() runs once per physics tick, the state transitions cleanly: true on the tick the key went down, false on every subsequent tick.
The reason _process() fails is that it can run faster than the physics tick rate. Between tick N and tick N+1, _process() might run two or three times, and all of those calls see the "just pressed" state that was set at tick N. The state does not clear until tick N+1.
"Use _process for continuous input like movement. Use _unhandled_input for discrete events like jumping, shooting, and interacting. This single rule eliminates an entire category of input bugs."
Related Issues
Input handling bugs tend to cluster together. If you are debugging action triggers, these related posts may help:
- Diagonal movement being faster than cardinal directions — another common input vector bug where raw axis values produce inconsistent speeds.
- Input actions not working after scene change — if your input stops responding entirely after switching scenes, the cause is different but equally frustrating.
- Double jump registering inconsistently — double-triggering input is often the underlying cause of unreliable double jump mechanics.