Summary
On native Wayland, every keystroke in Terax gets duplicated, and the duplication count doubles with each subsequent keystroke: the 1st keypress produces 1 character, the 2nd produces 2, the 3rd produces 4, the 4th produces 8, etc. This reproduces in both the terminal (PTY) and the separate AI chat text input, so it isn't scoped to the terminal/xterm.js input path specifically — it looks like a window/seat-level keyboard event handling bug.
Environment
- Terax version: 0.8.6 (RPM build)
- OS: Fedora, Wayland session (
XDG_SESSION_TYPE=wayland)
autocompleteEnabled and terminalWebglEnabled both toggled off — doubling persists regardless, ruling those features out.
Reproduction
- Launch Terax normally on a Wayland session.
- Open a terminal tab (or the AI chat input).
- Press any key repeatedly, one keystroke at a time.
- Character count per keystroke: 1 → 2 → 4 → 8 → 16 ... (doubling, not just +1 per press).
Also confirmed present in the AI chat text box using the same repeated-keystroke test (see old session titles like "dadada", "hdhdd", "helllo" in a long-lived terax-ai-sessions.json — those were this bug being hit organically over past sessions).
Workaround (confirmed fixes it)
Forcing the app onto XWayland instead of native Wayland eliminates the bug entirely:
This strongly suggests the bug is in Terax's native Wayland keyboard-binding code, not the X11 path.
Suspected root cause
strings on the terax binary shows direct references to wl_keyboard and wl_callback, indicating a hand-rolled Wayland client keyboard binding (rather than going through a mature toolkit's abstraction). A plausible mechanism: on a wl_seat::capabilities event (which can fire on focus changes, tab switches, etc.), the client calls get_keyboard() again without releasing the previously-bound wl_keyboard object first. This leaves multiple live keyboard listeners bound to the same seat — each subsequent keypress is then delivered once per listener. If handling a keystroke itself triggers another capabilities re-negotiation, the listener count doubles each time, producing the exact 1→2→4→8 pattern observed.
TERAX.md already documents a related class of bug (React 19 Strict Mode double-invoking useEffect, causing duplicate PTY spawns, mitigated with a SPAWN_LOCK mutex) — worth checking whether that mitigation covers the native keyboard-binding path, or whether this is a separate instance of the same underlying pattern (resource acquired again without releasing the prior instance).
Suggested fix direction
Ensure any existing wl_keyboard object is released (wl_keyboard::release) before binding a new one in response to wl_seat::capabilities, and/or guard against re-binding when a valid keyboard object is already held.
Happy to provide more diagnostics (journal logs, binary strings output) if useful.
Summary
On native Wayland, every keystroke in Terax gets duplicated, and the duplication count doubles with each subsequent keystroke: the 1st keypress produces 1 character, the 2nd produces 2, the 3rd produces 4, the 4th produces 8, etc. This reproduces in both the terminal (PTY) and the separate AI chat text input, so it isn't scoped to the terminal/xterm.js input path specifically — it looks like a window/seat-level keyboard event handling bug.
Environment
XDG_SESSION_TYPE=wayland)autocompleteEnabledandterminalWebglEnabledboth toggled off — doubling persists regardless, ruling those features out.Reproduction
Also confirmed present in the AI chat text box using the same repeated-keystroke test (see old session titles like "dadada", "hdhdd", "helllo" in a long-lived
terax-ai-sessions.json— those were this bug being hit organically over past sessions).Workaround (confirmed fixes it)
Forcing the app onto XWayland instead of native Wayland eliminates the bug entirely:
This strongly suggests the bug is in Terax's native Wayland keyboard-binding code, not the X11 path.
Suspected root cause
stringson theteraxbinary shows direct references towl_keyboardandwl_callback, indicating a hand-rolled Wayland client keyboard binding (rather than going through a mature toolkit's abstraction). A plausible mechanism: on awl_seat::capabilitiesevent (which can fire on focus changes, tab switches, etc.), the client callsget_keyboard()again without releasing the previously-boundwl_keyboardobject first. This leaves multiple live keyboard listeners bound to the same seat — each subsequent keypress is then delivered once per listener. If handling a keystroke itself triggers another capabilities re-negotiation, the listener count doubles each time, producing the exact 1→2→4→8 pattern observed.TERAX.mdalready documents a related class of bug (React 19 Strict Mode double-invokinguseEffect, causing duplicate PTY spawns, mitigated with aSPAWN_LOCKmutex) — worth checking whether that mitigation covers the native keyboard-binding path, or whether this is a separate instance of the same underlying pattern (resource acquired again without releasing the prior instance).Suggested fix direction
Ensure any existing
wl_keyboardobject is released (wl_keyboard::release) before binding a new one in response towl_seat::capabilities, and/or guard against re-binding when a valid keyboard object is already held.Happy to provide more diagnostics (journal logs, binary strings output) if useful.