[BUG] SGR mouse-report fragments (e.g. `;35;44M`) leak into the prompt as text when the session is busy — no focus switch or external editor

Status Open
Reported on v2.1.220
Maintainer reply ✓ Yes — bcherny
Activity 3 comments · opened Jul 30, 2026
💡 Likely answer: A maintainer (bcherny, collaborator) responded on this thread — see the highlighted reply below.

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code

What's Wrong?

While a session is busy (mid-stream of a long response, a heavy re-render, or a long-running tool call), scrolling the mouse wheel appends raw text to the prompt input. No key is pressed. Typical result:

> ;35;44M

The fragment is the tail of an SGR mouse report. Claude Code enables ESC[?1000h + ESC[?1006h, so the terminal sends every wheel/click event as:

ESC [ < Cb ; Cx ; Cy M

| Field | Meaning | In ;35;44M |
|---|---|---|
| Cb | button — 64/65 = wheel up/down | consumed with the ESC[< prefix |
| Cx | column | 35 |
| Cy | row | 44 |
| M | press/scroll terminator | M |

The give-away that this is a mouse event rather than keyboard corruption: the two numbers are screen coordinates and change as the pointer moves to a different part of the window.

The risk is submitting a polluted prompt — the junk lands in the input box and is easy to miss if you're typing.

Suspected root cause

The escape-sequence parser appears to time out mid-sequence under load. The binary contains escapeCodeTimeout (the standard ink/readline knob). On seeing ESC, the parser waits a bounded window for the remainder before deciding the byte was a lone Escape. When the event loop is blocked, the mouse report arrives split across reads and the timeout fires part-way through: the prefix (ESC[<64) is consumed as one token and the residual bytes (;35;44M) fall through and are treated as ordinary printable input.

That would explain why the artifact tracks session responsiveness rather than any particular keystroke or command — lag doesn't corrupt the bytes, it desynchronizes the state machine meant to swallow them.

Relationship to existing issues

This is the same defect class as the chain below, but with a different and much more mundane trigger — ordinary scrolling while the session is busy. No external editor, no IDE, no application focus switch is involved, which is what distinguishes it from all of these:

  • #42606 — CLAUDE_CODE_NO_FLICKER=1 + Ctrl-G external editor. Shows the same raw form (^[[<51;120;17M…). Auto-closed for inactivity with no fix.
  • #50032 — closed as a duplicate of #42606.
  • #80659 — focus switch after opening a plan in VS Code (iTerm2); chains back to #42606.

Since #42606 was closed for inactivity rather than fixed, the underlying parser behaviour appears to still be present.

Environment

  • Claude Code: 2.1.220
  • OS: macOS 24.6.0 (Apple Silicon)
  • Terminal: Warp
  • Renderer: "tui": "fullscreen"
  • Mouse modes requested by the TUI: ?1000h and ?1006h (?1002/?1003 motion tracking are not used, which matches the observation that leaks correlate with wheel/click rather than pointer movement)

Steps to Reproduce

Intermittent and load-dependent, so this is a "make it likely" recipe rather than a deterministic one:

  1. Run claude in a terminal (observed in Warp; the underlying parser behaviour is not obviously terminal-specific).
  2. Start something that keeps the session busy for a sustained period — a long streaming response or a long-running tool call.
  3. While output is actively streaming, scroll the mouse wheel over the window.
  4. Watch the prompt input box.

Observed: fragments such as ;35;44M accumulate in the input. Coordinates vary with pointer position.

Expected

Mouse reports are consumed by the input parser and never reach the prompt buffer, regardless of how busy the event loop is.

Note on the usual workaround

CLAUDE_CODE_DISABLE_MOUSE=1 is the commonly suggested mitigation, but it isn't usable under the fullscreen renderer: per #70724 the wheel is then handed to the prompt box as history navigation rather than falling back to native terminal scroll. So for fullscreen users there is currently no good mitigation for this leak.

View original on GitHub ↗

3 Comments

bcherny collaborator · 1 month ago

Reproduced and confirmed fixed — thank you for the precise analysis, which was exactly right about the parser timing out mid-sequence.

On 2.1.233 I could reproduce this deterministically without any load by feeding the terminal an SGR wheel report split across two writes (prefix ESC[<64, then ;35;44M ~150ms later): the residual ;35;44M lands in the prompt as literal text, exactly the artifact you showed. A report arriving in a single write is consumed cleanly, confirming the split-across-reads mechanism.

This was fixed in 2.1.239 (changelog: "Fixed mouse movement in browser-based terminals inserting text like 35;150;7M into the prompt when a mouse report arrived split across writes" — the fix applies to any terminal, not just browser-based ones). I verified on 2.1.243 that the same split report is now reassembled and consumed with nothing leaking into the prompt.

Please upgrade to 2.1.239 or later; closing as fixed. If you still see fragments on ≥2.1.239, a comment here with the version and terminal would be very welcome.

🤖 Generated with Claude Code

astanar01 · 1 month ago

Same behavior using macos default terminal. Using claude to launch multiple agents to plan/execute tasks bugs out the input.

<img width="1692" height="65" alt="Image" src="https://github.com/user-attachments/assets/276de585-4951-45cf-8fcb-74ac6fdfce47" />

Using v2.1.252

dev0zzz · 3 days ago

Still reproducible on 2.1.296, and not only for mouse reports.

Environment

  • Claude Code 2.1.296 (native), fullscreen renderer ("tui": "fullscreen")
  • WezTerm 20260716-195552-76b606ec, TERM=xterm-256color
  • Arch Linux, kernel 7.1.6, i3 (X11), no tmux, no SSH

What happens
Every time a long-running MCP tool call is in flight (a remote HTTP MCP server, calls taking 30–60 s, spinner showing Calling <server> · 46s…), escape-sequence fragments land in the prompt as literal text. Example from one call:

2M<35;43;41M49M;33;50M35;33;49M35;37;43M;30M<35;54;29M;43M5;64;45M35;66;33M<35;53;20M7;39M1;42M7;47M47M<35;74;43M<35;63;34M<35;55;28MMM[A[B<35;13;49M<35;5;47M1;51M<35;16;52M5;22;53M<35;43;55M[I;1M61;36M<35;58;39M52;43M<35;43;45M5;66;29M26M[0<35;75;3M69;18M35;62;27M

Besides the SGR mouse-motion reports (<35;x;yM, many with their prefix cut off), the same buffer contains [A[B (arrow keys) and [I (focus-in, mode 1004). So the split-across-reads problem from #76816 / this issue also hits keyboard CSI sequences and focus events, not just mouse reports. CLAUDE_CODE_DISABLE_MOUSE=1 would therefore only remove part of it.

Plain printable keys arrive fine; anything sent as an escape sequence while the session is busy can be torn apart. It happens across all sessions on this machine, consistently during these long tool calls.