Ctrl+key bindings ignored under non-Latin layouts on iTerm2: Claude Code overrides its own kitty push with modifyOtherKeys
Summary
On iTerm2, Ctrl+key bindings are silently ignored while a non-Latin keyboard layout (Thai, in my case) is active. The same keys work in the shell, and work in Claude Code under a Latin layout.
The cause is not the terminal. At startup Claude Code pushes kitty keyboard flags and then immediately enables xterm modifyOtherKeys, which wins on iTerm2. modifyOtherKeys reports the codepoint the key produces in the current layout and carries no layout-independent key identity, so no binding can match. The kitty flags Claude Code had just pushed include the bit that would have carried the US-layout key.
This is the same user-visible symptom as #58076, #36549, #40291 and #49758, which were all closed as stale or duplicate. I am filing it again because I have the measured mechanism rather than just the symptom.
Environment
| | |
|---|---|
| Claude Code | 2.1.266 |
| Terminal | iTerm2 3.6.11 and 3.7.0, both tested, identical behaviour |
| OS | macOS, Darwin 25.4.0 |
| Layout | Thai (macOS built-in), compared against ABC |
Reproduction
- Enable a non-Latin keyboard layout, e.g. Thai.
- Start Claude Code in iTerm2.
- Press Ctrl+A under a Latin layout. The binding fires.
- Switch to the non-Latin layout and press the same physical key with Ctrl. Nothing happens, no error, no character inserted.
Evidence
What Claude Code writes to the terminal at startup, byte offsets into its first output:
| offset | sequence | meaning |
|---|---|---|
| 43 | ESC [ < u | pop kitty keyboard stack |
| 47 | ESC [ > 5 u | push kitty flags 5 = disambiguate + report alternate keys |
| 52 | ESC [ > 4 ; 2 m | xterm modifyOtherKeys = 2 |
Reproduced by running Claude Code under a pty and capturing its output. The order is stable across runs, and identical with TERM_PROGRAM set to ghostty or TERM set to xterm-kitty.
What Claude Code receives, captured by a pty wrapper sitting in the same pty, tagged with the macOS input source that was active:
| key | layout | bytes received |
|---|---|---|
| Ctrl+A | ABC | ESC [ 27 ; 5 ; 97 ~ |
| Ctrl+A | Thai | ESC [ 27 ; 5 ; 3615 ~ |
97 is a and matches ctrl+a. 3615 is U+0E1F THAI CHARACTER FO FAN, the character that key produces under the Thai layout, and matches no binding.
For contrast, with no key reporting mode active the same keypress reaches a plain program as the byte 0x01 under both layouts, which is why the shell is unaffected.
Secondary finding
I filtered ESC [ > 4 ; 2 m out of Claude Code's output with a pty wrapper, to leave iTerm2 in the kitty mode Claude Code had just requested. Two things came out of it:
- iTerm2 does enter kitty mode, but does not honor flag 4, on 3.6.11 and on 3.7.0 alike. Ctrl+A under Thai arrives as
ESC [ 3615 ; 5 uwith no alternate-key section, so the base-layout key that would identify the physical key is still absent. Upgrading from 3.6.11 to 3.7.0, released 2026-09-08, changed nothing here. Per the kitty spec flag 4 takes effect whenever flag 1, 2 or 8 is set, and flag 1 is set here. - In kitty mode Ctrl+D stops working entirely, under a Latin layout too. It arrives as
ESC [ 100 ; 5 uand is ignored; repeated presses do nothing. Ctrl+C arrives in the same form,ESC [ 99 ; 5 u. Both are documented as hardcoded and appear to be matched on the raw0x04/0x03bytes only. This is a separate defect, and it is reachable by any terminal that ends up in kitty mode.
Suggested fixes
- Do not send
ESC [ > 4 ; 2 mafter a kitty push on terminals where the kitty push is expected to be honored. Sending both leaves the weaker protocol in effect. - Fall back to matching Ctrl bindings on the control byte, or on the physical key, rather than on the reported codepoint, so a non-Latin layout cannot break them.
- Handle the kitty CSI u form for the hardcoded Ctrl+C and Ctrl+D.
Happy to re-run any of the captures or test a build.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗