[BUG] Shifted punctuation arrives unshifted in WezTerm (2.1.247 regression)

Status Open
Reported on v2.1.247
Maintainer reply None cached
Activity 3 comments · opened Aug 27, 2026

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

View original on GitHub ↗

3 Comments

arnaudagent-droid · 3 days ago

Same regression on a different setup, with one extra data point: uppercase letters are lost here too, not only shifted punctuation.

  • Claude Code 2.1.248 (npm global) on native Windows 11 (not WSL) — TERM_PROGRAM=WezTerm, TERM=wezterm
  • WezTerm on Windows, enable_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 combo
  • Shift+letter yields the lowercase letter; Caps Lock is unaffected. Shifted punctuation is also wrong, consistent with the analysis above.
  • Reproduced on two separate Windows machines, both of which auto-updated 2.1.220 → 2.1.247/2.1.248 on 2026-08-27. 2.1.247 and 2.1.248 both affected.
  • Rolling back to a pre-2.1.247 build (2.1.220) restores correct behaviour; DISABLE_AUTOUPDATER=1 keeps 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 >5u flag change plus the unread group-2 field you identified.

I haven't yet captured the exact ESC [ … u sequence 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 your 47:63;130u capture.

staskorz · 2 days ago

The issue still persists in Claude Code 2.1.250.
Environment: Kubuntu 24.04 x64
WezTerm: 20240203-110809-5046fc22 (stable)

RTRowe · 7 hours ago

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

  • Claude Code 2.1.251 (native install) — one version newer than the last confirmation in this thread
  • WezTerm 20260714-220616-d96ba571, enable_kitty_keyboard = true
  • Windows 11 Pro 26200, native (not WSL), PowerShell 7
  • TERM_PROGRAM=WezTerm, TERM=xterm-256color (note: not wezterm, 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:

[A] LEGACY (no flags pushed)
    SHIFT + / :   3f    ?
    SHIFT + 1 :   21    !
    SHIFT + ; :   3a    :

[B] flags >1u  (what 2.1.246 asked for)
    SHIFT + / :   3f    ?
    SHIFT + 1 :   21    !
    SHIFT + ; :   3a    :

[C] flags >5u  (what 2.1.247+ asks for)
    SHIFT + / :   1b 5b 34 37 3a 36 33 3b 31 33 30 75    ESC[47:63;130u
    SHIFT + 1 :   1b 5b 34 39 3a 33 33 3b 31 33 30 75    ESC[49:33;130u
    SHIFT + ; :   1b 5b 35 39 3a 35 38 3b 31 33 30 75    ESC[59:58;130u

So WezTerm reports base:shifted for 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 the toUpperCase fallback 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_keyboard restores ?. 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:

  • Ctrl+J inserts a newline in every terminal, no config and no AltGr.
  • Or bind it in WezTerm, which is what I'm now running:

``lua
{ key = 'Enter', mods = 'SHIFT', action = wezterm.action.SendString '\n' },
``

Claude Code decodes CR (0x0d) as return → submit and LF (0x0a) as enter → newline, so sending a bare LF gives a line break with no kitty protocol involved.

Worth noting /terminal-setup isn'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 >1u and 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:

printf '\033[>5u'; od -c; printf '\033[<u\n'