Skip to content

Assertion failed: simulate-keyboard #2835

Description

@kuluna

Assertion failed: simulate-keyboard

Written with AI.

Summary

simulate-keyboard triggers Assertion failed when an enabled action receives input and its performed callback disables its own action map. The CLI still returns Success: true and PressEdgeObserved: true.

Steps to reproduce

  1. Open a fresh Unity project with Input System and uLoop installed.
  2. ave the following snippet as setup.cs.txt in the project root (outside Assets).
using UnityEngine.InputSystem;


foreach (var old in Resources.FindObjectsOfTypeAll<InputActionAsset>().Where(x => x.name == "UloopAssertionRepro"))
{
    old.Disable();
    UnityEngine.Object.DestroyImmediate(old);
}
var asset = ScriptableObject.CreateInstance<InputActionAsset>();
asset.name = "UloopAssertionRepro";
var map = asset.AddActionMap("Repro");
var action = map.AddAction("PressB", InputActionType.Button, "<Keyboard>/b");
bool disable = true;
action.performed += context =>
{
    Debug.Log("UloopAssertionRepro: performed; disable=" + disable);
    if (disable) map.Disable();
};
map.Enable();
return "Armed: " + action.enabled + "; disable=" + disable;
  1. Run from the project root:
uloop control-play-mode --action Play
uloop execute-dynamic-code --code-file setup.cs.txt
uloop simulate-keyboard --action Press --key B
uloop get-logs --log-type Error --search-text 'Assertion failed' --include-stack-trace --max-count 20

Expected: the callback disables the map without an assertion.
Actual: one assertion is logged. To repeat, rerun the setup command before pressing B.

Example use cases

  • Visual novel: opening backlog switches from the message input map to the backlog map, disabling the original map inside the callback.
  • Similar patterns: opening a pause menu or inventory disables gameplay controls; entering a dialogue choice switches input maps.

Environment

  • uLoop Unity package: 3.6.3
  • uLoop CLI (uloop --version): 3.3.1
  • macOS: 26.7
  • Unity: 6000.3.23f1
  • Input System: 1.20.0

Unity log:
Assertion failed
UnityEngine.InputSystem.LowLevel.InputState:Change (UnityEngine.InputSystem.InputDevice,UnityEngine.InputSystem.LowLevel.InputEventPtr,UnityEngine.InputSystem.LowLevel.InputUpdateType)
io.github.hatayama.UnityCliLoop.FirstPartyTools.KeyboardKeyStateService:SetKeyState (UnityEngine.InputSystem.Keyboard,UnityEngine.InputSystem.Key,bool) (at ./Library/PackageCache/io.github.hatayama.uloopmcp@90156fbc32de/Editor/FirstPartyTools/SimulateKeyboard/Application/KeyboardKeyState.cs:97)
io.github.hatayama.UnityCliLoop.FirstPartyTools.KeyboardKeyState:SetKeyState (UnityEngine.InputSystem.Keyboard,UnityEngine.InputSystem.Key,bool) (at ./Library/PackageCache/io.github.hatayama.uloopmcp@90156fbc32de/Editor/FirstPartyTools/SimulateKeyboard/Application/KeyboardKeyState.cs:184)
io.github.hatayama.UnityCliLoop.FirstPartyTools.KeyboardInputActionExecutor/<>c__DisplayClass0_0:<ExecutePress>b__1 () (at ./Library/PackageCache/io.github.hatayama.uloopmcp@90156fbc32de/Editor/FirstPartyTools/SimulateKeyboard/KeyboardInputActionExecutor.cs:65)
io.github.hatayama.UnityCliLoop.FirstPartyTools.InputSystemConfiguredUpdateApplier/<>c__DisplayClass7_0:<CreateConfiguredUpdateCallback>b__0 () (at ./Library/PackageCache/io.github.hatayama.uloopmcp@90156fbc32de/Editor/FirstPartyTools/Common/InputSystem/InputSystemConfiguredUpdateApplier.cs:116)
UnityEngine.InputSystem.LowLevel.NativeInputRuntime/<>c__DisplayClass13_0:<set_onBeforeUpdate>b__0 (UnityEngineInternal.Input.NativeInputUpdateType)
UnityEngineInternal.Input.NativeInputSystem:NotifyBeforeUpdate (UnityEngineInternal.Input.NativeInputUpdateType) (at /Users/bokken/build/output/unity/unity/Modules/Input/Private/Input.cs:117)

Activity

  1. hatayama commented on Sep 21, 2026

    @hatayama
    Owner

    Fixed in #2851 (merged to main; it will ship in the next Unity package release).

    Root cause: uloop writes simulated input with InputState.Change outside the Input System's event-processing loop. When a performed callback disables its own action map there, the Input System removes the state monitor immediately instead of deferring it, and DynamicBitfield.ClearBit then trips a bare Debug.Assert — the Assertion failed you saw. Real device input goes through the event loop, where that removal is deferred, so it does not hit this.

    What changed: uloop now suppresses that one assertion, and only when its call stack shows it came from the Input System's monitor-removal path, so your own Debug.Assert calls stay visible. simulate-keyboard and simulate-mouse-input return a Warning when this happened. Your callback still runs and the map is still disabled.

    One caveat the warning also mentions: if other actions are bound to the same control, they may miss that input. If that matters, switch action maps on the next frame instead of inside the callback. The underlying behavior is on the Input System side, and I'm reporting it to Unity.

  2. hatayama commented on Sep 28, 2026

    @hatayama
    Owner

    Reported the underlying Input System defect to Unity as IN-155045 (state change monitors miss notifications and DynamicBitfield.ClearBit asserts when a monitor is removed inside its own notification while state is applied with InputState.Change outside event processing). uloop's workaround ships in v3.7.0 (#2851, #2856, #2904).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions