[BUG] Mouse-tracking modes left enabled after session exit — bare shell floods with SGR sequences on mouse move (macOS Terminal.app)
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
- Run a Claude Code session in macOS Terminal.app.
- 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). - 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.
Showing cached comments. Read the full discussion on GitHub ↗
4 Comments
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.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
sshinto a remote host (Ubuntu 24.04 on EC2 in my case).?1000/?1002/?1003/SGR?1006).nik@m5 ~ %prompt).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:
(button 35/64/65 = move/scroll/release events; trailing
col;rowpairs trace the cursor path; final0;32;8mis a button-release.)Why this matters
\e[?1000l\e[?1002l\e[?1003l\e[?1006lon 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 surgicalprintf '\e[?1000l\e[?1002l\e[?1003l\e[?1006l'.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:/exit→ mouse tracking (1000/1002/1003/1006) is turned off before the shell prompt returns.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 withM65;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 ofclaude doctor? A note on whether the shell prompt appeared normally before the flood started would also help.🤖 Generated with Claude Code
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.