[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
3 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: