[BUG] Shifted punctuation arrives unshifted in WezTerm (2.1.247 regression)
Since 2.1.247, shift+/ inserts / instead of ? in the prompt input under WezTerm. Shifted letters are fine, which is what makes it easy to miss for a while. 2.1.246 on the same machine and the same terminal is unaffected.
2.1.247 asks for kitty keyboard flags 5 where 2.1.246 asked for 1. Flag 4 requests alternate keys, so WezTerm now answers shift+/ with ESC [ 47:63;130u rather than a bare ? byte. 47 is /, 63 is the ? that was wanted, and 130 is shift with num lock on.
The decoder captures that middle field and then never reads it:
yh = /^\x1b\[(\d+)(?::(\d*)(?::(\d+))?)?(?:;(\d+))?u/
// group 1 = 47, group 2 = 63, group 3 = base layout key, group 4 = modifiers
let s = parseInt(l[1], 10) // 47
let m = l[3] ? parseInt(l[3], 10) : void 0
let h = f.ctrl && s > 127 && m !== void 0 ? m : s
return { name: Pb(f, h) ?? Ub(h), shift: f.shift, ... }
Text insertion then tries to re-derive the character from the name and the shift flag, by case mapping:
if (o.length === 1) { // one raw byte, the pre-2.1.247 path
let l = o.charCodeAt(0)
if (l >= 32 && l !== 127) return o // '?' byte -> '?'
}
if (i) {
if (e.shift && i.length === 1) {
let l = i.toUpperCase()
if (l !== i && l.length === 1) return l // 'a' -> 'A'
}
return i // '/' -> '/'
}
'a'.toUpperCase() is 'A', so letters come out right. '/'.toUpperCase() is still '/', the guard fails, and the unshifted character is returned. The 13-byte sequence also skips the single-byte path above, which is what handled this before. By that route every shifted key whose shifted form isn't an uppercase letter is affected, though ? is the only one I've checked.
To reproduce, set enable_kitty_keyboard = true in ~/.wezterm.lua (WezTerm defaults it off), open a prompt, and press shift+/. What the terminal sends can be confirmed outside Claude Code with the following, pressing shift+/ then Enter then Ctrl+D:
printf '\033[>5u'; od -c; printf '\033[<u\n'
Reading group 2 where it exists would fix it, as would requesting flag 16 so the terminal reports the associated text.
One caveat on attribution. My WezTerm is a nightly built the same day as 2.1.247, so I can't rule out movement on that side as well. What I did verify is the request, where >5u appears in the 2.1.247 binary and not in 2.1.246.
Environment:
- Claude Code 2.1.247 (native install) broken, 2.1.246 fine
- WezTerm
20260826-113711-78cd82db,enable_kitty_keyboard = true - Ubuntu 24.04.4 on WSL2, kernel
6.18.33.2-microsoft-standard-WSL2 TERM=wezterm,TERM_PROGRAM=WezTerm
12 Comments
Same regression on a different setup, with one extra data point: uppercase letters are lost here too, not only shifted punctuation.
TERM_PROGRAM=WezTerm,TERM=weztermenable_kitty_keyboard = true— kept on deliberately because Shift+Enter for a newline is needed, and on an AZERTY (French) layout the usual\+Enter fallback is an AltGr combo2.1.220 → 2.1.247/2.1.248on 2026-08-27.2.1.247and2.1.248both affected.2.1.247build (2.1.220) restores correct behaviour;DISABLE_AUTOUPDATER=1keeps it pinned.So on this AZERTY / native-Windows path the "
'a'.toUpperCase()saves letters" branch doesn't hold — the case-mapping guard appears to fail for letters as well. Looks like a regression of the earlier kitty-keyboard Shift+letter bugs (#39479, #49359), now re-triggered by the>5uflag change plus the unread group-2 field you identified.I haven't yet captured the exact
ESC [ … usequence WezTerm emits for Shift+A on this layout — glad to, if it helps tell whether it's the base-layout-key field (group 3) or the modifier value that differs from your47:63;130ucapture.The issue still persists in Claude Code
2.1.250.Environment: Kubuntu 24.04 x64
WezTerm:
20240203-110809-5046fc22(stable)Another native-Windows data point, and one that doesn't fit the "every shifted key whose shifted form isn't an uppercase letter is affected" scope in the OP.
Environment
20260714-220616-d96ba571,enable_kitty_keyboard = trueTERM_PROGRAM=WezTerm,TERM=xterm-256color(note: notwezterm, unlike the OP)Only
?is affected here. Shift+1 →!and Shift+; →:both insert correctly in the prompt, and capital letters are fine. Only Shift+/ yields/.That's worth flagging, because the terminal encodes all three identically. Captured with a raw-stdin reader in the same WezTerm, pushing the same flags Claude Code asks for:
So WezTerm reports
base:shiftedfor all three —47:63,49:33,59:58— and Claude Code recovers!and:correctly but not?. Whatever drops the group-2 field looks narrower than a blanket "unshifted character returned". It may be worth checking whether/takes a different path from the other two, given it's also the slash-command trigger, rather than assuming thetoUpperCasefallback accounts for all of it.Between the three reports there are now three distinct scopes: OP (shifted punctuation, letters fine), @arnaudagent-droid (shifted punctuation and uppercase letters lost, AZERTY), and this one (only
?). Same encoding, different outcomes.The OP's workaround is confirmed — commenting out
enable_kitty_keyboardrestores?. One detail that saves a restart: it takes effect on WezTerm's config hot-reload, with no restart of WezTerm or of the running Claude Code session, because the setting gates whether WezTerm honors the pushed flags at all.---
@arnaudagent-droid — you mentioned keeping the protocol enabled because you need Shift+Enter and the
\+Enter fallback is an AltGr combo on AZERTY. Two ways to get the newline without the protocol:``
lua
``{ key = 'Enter', mods = 'SHIFT', action = wezterm.action.SendString '\n' },
Claude Code decodes CR (
0x0d) asreturn→ submit and LF (0x0a) asenter→ newline, so sending a bare LF gives a line break with no kitty protocol involved.Worth noting
/terminal-setupisn't an option for either of us — the docs list only VS Code, Cursor, Devin Desktop, Alacritty and Zed; WezTerm is in the "works without setup" column precisely because it normally speaks this protocol.---
One trap for anyone reproducing this: probe with the same flags Claude Code actually sends. My first capture used
>1uand returned a clean literal?, which wrongly exonerated the terminal and cost me a fair bit of time. The OP's probe is the correct one:Issue still in
v2.1.252.And for me all "Shift + {something}" do not work.
Shift+1 : does not work
Shift+-: does not work
Shift+Enter: does not work
Using a QWERTZ Keyboard with
config.enable_kitty_keyboard = true.Workaround - add the following to your
~/.wezterm.lua:I'm using it for a few days already, didn't notice any regressions, other apps keep working fine.
Running @staskorz's workaround here on 2.1.252, and it does the job, thanks for posting it. It can be halved though. The
mods = 'NONE'bindings never fire for punctuation, so only theSHIFThalf is doing anything.wezterm normalises the shift modifier on both sides of the lookup, in
normalize_shift(wezterm-input-types/src/lib.rs). That only rewritesKeyCode::Char(c)wherecis an ASCII letter, and everything else falls through its_ => (key, modifiers)arm with SHIFT intact. So shift+/ arrives as('?', SHIFT)and matches theSHIFTentry only.Config::key_bindingsputs the configured side through the same function, so{key = '?', mods = 'NONE'}is stored under('?', NONE)and nothing ever reaches it. wezterm's own docs bind shifted punctuation the same way,key = '|', mods = 'LEADER|SHIFT'.It flips for letters. Maybe useful for @arnaudagent-droid and @cebor, since you're both losing capitals on top of the punctuation. A lowercase char plus SHIFT gets rewritten to the uppercase char with SHIFT removed, so the binding wants the uppercase key:
Either mods form works there, since both sides normalise to the same pair. Won't cover anything reached by AltGr on AZERTY though, that arrives with different mods and neither binding matches.
I created a repo that patches the claude binary to fix the issue https://github.com/0xGREG/claude-kitty-patch
This is resolved in v2.1.269.
https://code.claude.com/docs/en/changelog#2-1-269
For me this issue is not resolved. Still in 2.1.272.
With
config.enable_kitty_keyboard = trueit does not work.@cebor OK, I've reopened the issue for your case :)
Confirming the regression is still present after the 2.1.269 fix, on a distinct key subset from both the OP and RTRowe.
Environment: Claude Code 2.1.270 (npm global), Windows 11 native (not WSL), WezTerm 20240203-110809-5046fc22,
enable_kitty_keyboard = true, AZERTY (French) layout.Still broken: Shift+letter yields lowercase, no uppercase at all (same as my Aug 28 report on 2.1.248). Shifted punctuation:
)=,*ù<.Fine:
./§with Shift. Digits (Shift+top-row on AZERTY) unaffected.Between the reports there are now at least four distinct scopes on the same encoding: OP (punctuation only, letters fine), mine (punctuation + all letters, AZERTY), RTRowe (only Shift+/), cebor (QWERTZ, broadly broken). The 2.1.269 fix clearly didn't close all of these. Rolling back to 2.1.220 again as a stopgap.
This is a bug in the kitty protocol implementation of WezTerm, WezTerm nightly resolves the issue. Otherwise use my patcher https://github.com/0xGREG/claude-kitty-patch that uses legacy kitty protocol.