[BUG] Mouse-tracking modes left enabled after session exit — bare shell floods with SGR sequences on mouse move (macOS Terminal.app)

Status Closed — not planned
Reported on v2.1.197
Maintainer reply ✓ Yes — bcherny
Activity 5 comments · opened Jul 1, 2026 · closed Aug 23, 2026
💡 Likely answer: A maintainer (bcherny, collaborator) responded on this thread — see the highlighted reply below.

Summary

When a Claude Code session exits back to the shell, the terminal's mouse-tracking modes (?1000/?1002/?1003, SGR ?1006) are not disabled on exit. Control returns to a bare zsh prompt with tracking still active, so every subsequent mouse movement over the window is echoed as raw SGR escape sequences onto the command line. Within seconds of moving the mouse, the prompt is flooded with thousands of <button>;<col>;<row>M fragments.

This is the classic "restore terminal modes on exit" defect, but for the clean-exit-to-shell path on native macOS Terminal.app — which existing reports don't cover (see "Related" below).

Environment

| | |
|---|---|
| Claude Code | 2.1.197 |
| OS | macOS 26.5.1 |
| Terminal | Apple Terminal.app build 470.2 (native, not a browser/WSL terminal) |
| TERM | xterm-256color |
| Shell | zsh |

Repro

  1. Run a Claude Code session in macOS Terminal.app.
  2. Let the session exit back to the shell prompt (normal exit; the session in my case had a task title set — window title read Investigate GitHub issue #1987).
  3. Move the mouse over the terminal window.

Expected

On exit, Claude Code disables the mouse-tracking modes it enabled (printf '\e[?1000l\e[?1002l\e[?1003l\e[?1006l'), leaving the shell in a clean state. Mouse movement produces nothing.

Actual

Tracking is left on. The bare zsh prompt receives raw mouse reports as literal text and echoes them, e.g.:

M65;92;11M65;92;11M65;92;11M64;92;11M64;92;11...M35;27;5M

(button 64/65 = scroll/move events; the trailing col;row pairs drift as the mouse moves — a literal trace of the cursor path). The prompt fills with thousands of these until reset is run.

Workaround

reset, or the surgical version:

printf '\e[?1000l\e[?1002l\e[?1003l\e[?1006l'

Related

  • #58653 (closed) — same class of leak but during agent view while scrolling, not on exit
  • #59720 — "leak on kill" but Windows + agent-view-specific, bundled with other defects
  • #72268 / #72269 — leak on resize/suspend, explicitly WSL2-only ("does NOT reproduce on macOS")
  • #66289 — browser-based xterm.js terminals only

None cover the native-macOS clean-exit path, which is what this reports: the fix is ensuring the mouse-mode teardown runs on the normal exit handler for all platforms.

View original on GitHub ↗

4 Comments

jaypaulb · 1 month ago

Adding a second environment + a crash-path variant of this bug.

Environment: Ubuntu (Linux 6.17), Zellij 0.44.3, TERM=xterm-256color, Claude Code v2.1.199.

Repro path: Claude Code crashed mid-session with an "aborted" error (not a clean exit). The mouse-tracking modes (1000/1002/1003/1006) were left enabled, and every mouse movement afterwards flooded the shell with SGR fragments (35;48;27M35;46;27M... — button 35 = motion events, so any-event tracking 1003 was still active). Zellij passes the reports straight through to the dead pane's shell.

So this isn't only the clean-exit path on macOS Terminal.app — the crash/abort path has the same leak on Linux under a multiplexer. A robust fix probably needs the DECRST sequences in a crash/exception handler (and ideally a signal handler), not just the normal shutdown path.

Workaround confirmed working here: printf '\e[?1000l\e[?1002l\e[?1003l\e[?1006l' typed blind into the spam.

edmiidz · 1 month ago

Follow-up: reproduced across an SSH boundary (stronger/cleaner repro)

Hit this again and captured the exact context. In this instance the leak came from **Claude Code running on a remote host over SSH, and it left the local** macOS Terminal.app in mouse-tracking mode after the remote process exited. This is a more concrete repro than the original report and points more precisely at the missing teardown.

Repro

  1. In macOS Terminal.app (local shell, zsh), ssh into a remote host (Ubuntu 24.04 on EC2 in my case).
  2. On the remote host, start Claude Code and let it run (it enables mouse tracking ?1000/?1002/?1003/SGR ?1006).
  3. Let the remote Claude Code session exit back to the remote shell prompt (normal exit).
  4. Return control to the local shell (SSH session ends / you're back at the local nik@m5 ~ % prompt).
  5. Move the mouse over the Terminal window.

Actual

The local terminal is still in mouse-tracking mode — the remote Claude Code never emitted the disable sequences on exit, and nothing in the SSH teardown restored local terminal state. Every mouse move over the window is echoed as raw SGR reports into the bare local prompt, and the terminal bell fires repeatedly (audible beeping). Sample of the flood landing at the local prompt:

nik@m5 ~ % >....
...64;37;10M64;37;10M64;37;10M35;38;10M64;38;10M...35;32;8M0;32;8M0;32;8m

(button 35/64/65 = move/scroll/release events; trailing col;row pairs trace the cursor path; final 0;32;8m is a button-release.)

Why this matters

  • It confirms the defect is missing mouse-mode teardown on exit, independent of platform — here the terminal being corrupted is macOS Terminal.app, but the process that enabled tracking exited on a remote Linux host.
  • It explains an "only one window is affected" pattern: only the window that had hosted a Claude Code session (local or over SSH) is left dirty.
  • A well-behaved TUI should emit \e[?1000l\e[?1002l\e[?1003l\e[?1006l on every exit path (normal, signal, and process teardown) so that even over SSH the sequences reach and clean the controlling terminal.

Environment (this occurrence)

| | |
|---|---|
| Local terminal | macOS Terminal.app build 470.2, macOS 26.5.1, zsh, TERM=xterm-256color |
| Remote host | Ubuntu 24.04.4 LTS (EC2), reached via ssh |
| Claude Code | ran on the remote host |

Workaround (unchanged)

Run in the affected window: reset, or the surgical printf '\e[?1000l\e[?1002l\e[?1003l\e[?1006l'.

bcherny collaborator · 15 days ago

Thanks for the detailed report (and the follow-ups). I tried to reproduce this on the current release, 2.1.233 (macOS, TERM=xterm-256color, TERM_PROGRAM=Apple_Terminal, zsh), watching the terminal's mouse-tracking state directly:

  • Start Claude Code, run a turn so a task title is set, then /exit → mouse tracking (1000/1002/1003/1006) is turned off before the shell prompt returns.
  • Exit with Ctrl+C twice → same, tracking off.
  • Kill the process with SIGTERM → same, tracking off.

In each case the disable sequences (\e[?1006l\e[?1003l\e[?1002l\e[?1000l) are emitted on exit, so I couldn't get the shell to be flooded with M65;92;11M… fragments. The exit-time teardown runs on the normal exit, Ctrl+C, and signal paths; a hard kill (SIGKILL/OOM) or a dropped SSH connection can't be cleaned up by any program, and that would explain the SSH variant.

If you still see this on 2.1.233 or later, could you share: the exact Claude Code version (claude --version), how the session ended (/exit, Ctrl+C, Ctrl+D, crash message, closed tab), whether it was a fullscreen/agent-view session, and the output of claude doctor? A note on whether the shell prompt appeared normally before the flood started would also help.

🤖 Generated with Claude Code

github-actions[bot] · 15 days ago

We weren't able to reproduce this. Could you provide steps to trigger the issue — what you ran, what happened, and what you expected? This issue will be closed automatically if there's no activity within 7 days.

Showing cached comments. Read the full discussion on GitHub ↗