Quick answer: Call action.Enable() in OnEnable, use ReadValue for continuous inputs and performed callbacks for discrete actions (not both for the same action), and always unsubscribe in OnDisable. These three patterns fix most “input not updating” bugs in Unity’s Input System.
Unity’s new Input System is a big step up from the legacy Input.GetKey API — until something stops working and you have no idea why. Actions return zero, callbacks never fire, rebinds do nothing. The fix is almost always one of three mechanical issues, and knowing them saves hours of guessing.
The Symptom
You set up an Input Action asset, wire a script to read it, press the key, and nothing happens. Other symptoms:
action.ReadValue<Vector2>()always returnsVector2.zero.- The
performedcallback never fires even though the key is bound. - Input works in one scene and stops after a scene load.
- Input works in editor but not in the build (or vice versa).
Cause 1: Action Not Enabled
Every Input Action is created disabled. Until you call Enable(), the action produces the default value for its type and fires no callbacks.
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerController : MonoBehaviour
{
[SerializeField] private InputActionReference moveAction;
[SerializeField] private InputActionReference jumpAction;
void OnEnable()
{
moveAction.action.Enable();
jumpAction.action.Enable();
jumpAction.action.performed += OnJump;
}
void OnDisable()
{
jumpAction.action.performed -= OnJump;
moveAction.action.Disable();
jumpAction.action.Disable();
}
void Update()
{
Vector2 move = moveAction.action.ReadValue<Vector2>();
transform.Translate(move * 5f * Time.deltaTime);
}
void OnJump(InputAction.CallbackContext ctx)
{
Debug.Log("Jumped");
}
}
The PlayerInput component handles this automatically if you use it. If you reference an Input Action Asset directly, you must call Enable/Disable yourself.
Cause 2: Mixing ReadValue and Callbacks for the Same Action
Unity lets you both subscribe to performed and call ReadValue on the same action. In practice, mixing them produces confusing bugs where the callback fires but the polled value is zero, or the other way around.
The rule: one action, one consumption pattern. For continuous inputs like movement sticks and mouse look, poll with ReadValue every frame. For discrete inputs like jump, shoot, pause, interact, subscribe to the performed callback. Mixing them on the same action means one of the two paths will catch events the other misses.
If you need both styles — for example, a fire action that also needs a “hold to charge” visual — create two actions: a fire event-based action and a fireHeld polled action, bound to the same physical key.
Cause 3: Forgetting to Unsubscribe
Every += in OnEnable must be matched by a -= in OnDisable. Forgetting this does not crash immediately — it creates a leak. When the object is destroyed or the scene reloads, the subscription persists on the action, still pointing at the destroyed script. The next time the action fires, Unity calls a method on a null object and throws a MissingReferenceException.
The error is easy to miss because it fires at input time, not at scene load time. Always match your subscriptions. Use a one-line helper if you have many:
void OnDisable()
{
jumpAction.action.performed -= OnJump;
fireAction.action.performed -= OnFire;
interactAction.action.performed -= OnInteract;
moveAction.action.Disable();
jumpAction.action.Disable();
fireAction.action.Disable();
interactAction.action.Disable();
}
Scene Load Gotchas
Input Action Assets are ScriptableObjects. Reference counts are shared across scenes by default. If a script in scene A enables an action and scene B loads, the action is still enabled until something disables it. This usually works but produces bugs when two copies of the same script (one in a DontDestroyOnLoad object, one in the new scene) subscribe to the same action and fire twice.
The safest pattern is to have exactly one PlayerInput component (marked DontDestroyOnLoad) that owns all actions, and have other scripts read input through it instead of subscribing directly.
Debugging: Input Debugger
Unity ships a built-in Input Debugger under Window → Analysis → Input Debugger. Open it while playing and you can see every device, every action, every binding, and whether each action is currently enabled. If your action is listed as disabled, you found the bug. If it is enabled but the value is zero, the binding is wrong — check the Input Action Asset for the correct path.
Verifying the Fix
Add a Debug.Log of every action value in Update, build the game, and press every bound key. If the log prints the expected values, your actions are wired correctly. If any value stays at zero, that action is either disabled or bound to a path that does not match your device.
“The new Input System is powerful but opinionated. Enable, consume consistently, unsubscribe — three rules that fix 80% of the bugs people file about it.”
Related Issues
For input that works in editor but not build, see Unity input system not working. For new UI button input, see Unity new input system UI not working. For broader controller debugging, see how to debug controller dead zone issues.
The Input Debugger is the single most-useful tool in the new Input System. If an action does not work, open it before you change any code.