Input line: the first typed character is drawn one column left and cannot be deleted

Status Open
Maintainer reply None cached
Activity 0 comments · opened Jul 29, 2026

Version: Claude Code v2.1.220
Platform: Windows 10 (build 19045), PowerShell 7 / Windows PowerShell 5.1
Terminal: reproduced in an xterm.js host; the cause is in the bytes Claude Code emits, not in the terminal — see Evidence below.

What happens

Type merhaba at Claude Code's prompt. The input line renders

❯m erhaba          instead of      ❯ merhaba

The first character sits where the separator space belongs, one column left of the rest, and it cannot be deleted — backspace removes the text but leaves it behind. Clearing the input entirely leaves the stray character on screen, so the next thing typed appears after it.

Only the live input line is affected. The history echo of a submitted prompt renders correctly, and so does the greyed-out suggestion line — both of which are drawn in full rather than incrementally.

The bytes

Recorded from a raw pty at 93×36. The empty prompt is drawn as

❯<U+00A0>Try "fix typecheck errors"   … then …   CSI 33;3H   CSI ?25h

So: chevron at column 1, a non-breaking space at column 2 as the separator, the hint from column 3, and the cursor parked at column 3.

On the first keystroke Claude Code emits

SI   CSI 25 l   BACKSPACE   m   CSI K   …redraw of the divider and footer…   CSI 33;4H   CSI ?25h

The backspace moves the cursor from column 3 to column 2, m is written there — over the separator — and the cursor is then parked at column 4, one past what was just written.

The backspace is what is wrong, and the frame proves it: the prompt draw parks the cursor at column 3, which is exactly where the typed character belongs, and the frame ends by parking at column 4, which is correct for a character written at column 3. Writing m at column 3 and erasing the rest would have been right. The BACKSPACE is a movement that was not needed: it steps off the correct column before writing.

Column 3 is never written and never erased — the CSI K afterwards always runs from column 3 or later — which is why the stray character is unreachable.

Two smaller oddities in the same frame, possibly unrelated:

  • The cursor is hidden with CSI 25 l — no ? — which is not DECTCEM. It is shown again with the correct CSI ?25h.
  • A bare SI (U+000F) precedes each frame.

Evidence that this is not the terminal

The same frame was replayed into pyte, a VT emulator written in Python with no relationship to xterm.js:

st.feed("❯ Try"); st.feed("\x1b[1;3H")
st.feed("\x0f"); st.feed("\x1b[25l"); st.feed("\x08"); st.feed("m")
st.feed("\x1b[K"); st.feed("\x1b[1;4H"); st.feed("e")
# renders '❯m e'  ->  U+276f U+006d U+0020 U+0065

Character for character what the reporting terminal shows.

Also ruled out on the terminal side: character loss (all keystrokes reach the pty, including non-ASCII), the key handler, key bindings, glyph rescaling, the WebGL and DOM renderers, Unicode 6 versus 11 width tables, xterm's windowsPty ConPTY compatibility option, and the xterm version (6.0.0 and 6.1.0-beta.292 behave identically).

Variables eliminated on Claude Code's side

Each of these was tested by capturing the first-keystroke frame from two raw ptys at 93×36, identical except for the one variable:

  • TERM_PROGRAMstation versus vscode: byte-identical drawing, apart from the synchronized-output wrapper (CSI ?2026h / l) that is added only for terminals on the recognised list.
  • The IDE connection — a live CLAUDE_CODE_SSE_PORT pointing at a running VS Code extension server versus CLAUDE_CODE_AUTO_CONNECT_IDE=false: byte-identical, 287 bytes each.
  • CLAUDE_CODE_NO_FLICKER=1 — byte-identical, same 287 bytes.

So the artifact is unconditional in what Claude Code emits.

Reproducing

claude            # in any trusted directory
merhaba           # type it; do not paste

The stray character appears on the first keystroke of a fresh input line while the hint is showing — that is the state whose redraw contains the spurious backspace. Typing into a line that already has text is drawn correctly, which is what makes the bug read as intermittent.

View original on GitHub ↗