[BUG] Claude code spitting out random characters in user input area while running

Open 💬 18 comments Opened Jun 2, 2025 by somasays

Environment

  • Platform (select one):
  • [X] Anthropic API
  • [ ] AWS Bedrock
  • [ ] Google Vertex AI
  • [ ] Other: <!-- specify -->
  • Claude CLI version: 1.0.6
  • Operating System: MacOS 15.5
  • Terminal: iTerm2

Bug Description

<img width="1002" alt="Image" src="https://github.com/user-attachments/assets/9523053c-5b55-451e-9ab2-90eced22e36f" />

Spitting out random characters in user input area while running.

Steps to Reproduce

  1. ask claude to execute something for a python source code in a pipenv based project
  2. when it comes out with python -m xxxxxxx ask it use pipenv run

Expected Behavior

The user input area is clear

Actual Behavior

You see random escape sequence characters in the screenshot in the user input area

View original on GitHub ↗

18 Comments

somasays · 1 year ago

<img width="895" alt="Image" src="https://github.com/user-attachments/assets/9694d78f-c6cf-42d0-acf5-eeca56104081" />
issue persists

claude --version 1.0.10 (Claude Code)
sygi · 1 year ago

I also observed this in the newest CC. I observe the following behavior:

  1. the random characters still appear in the terminal after killing claude (but this only happens in the CC-infected terminals)
  2. they are generated when I move the mouse or press its buttons
FBakkensen · 1 year ago

I have the same issue after CC have executed some python code

nerdo · 1 year ago

I have the same issue in 1.0.51.

I actually asked Claude desktop about it and this was the response:

This looks like your terminal is receiving and displaying raw mouse tracking escape sequences. This happens when an application enables mouse tracking mode but doesn't properly disable it when exiting.

Here are a few ways to fix this:

  1. Quick reset - Try one of these:

``bash
reset
`
or
`bash
tput reset
``

  1. Disable mouse tracking manually:

``bash
printf '\033[?1000l'
``

  1. If you're using tmux, it might be the culprit. Try:

``bash
tmux refresh-client -c
``

  1. Check if a program crashed - Some programs (like vim, less, or certain TUI applications) enable mouse tracking. If they crash or exit abnormally, they might leave the terminal in this state.
  1. Kitty-specific reset:
  • Press Ctrl+Shift+Delete to reset the terminal
  • Or use kitty @ send-text --match=active '\x1b[?1000l'

To prevent this in the future:

  • Make sure programs exit cleanly (don't force-kill TUI applications)
  • Update your programs that use mouse input
  • Consider adding this to your shell config (e.g., ~/.bashrc or ~/.zshrc):

``bash
# Reset mouse tracking on each prompt
PROMPT_COMMAND='printf "\033[?1000l"'
``

The escape sequences you're seeing (like M35;261;21M35;261;20) are the raw mouse position data that should normally be hidden and processed by applications.

---

When this happens, even when I exit claude the terminal gets littered as I move the mouse. So I exited claude and tried option #2 and it stopped the behavior - until I loaded claude again.

For now I've disabled auto updating claude but I'd really like to continue getting updates, so I hope this gets addressed soon.

Theaxiom · 11 months ago

This is very annoying, experience it here as well.

<img width="1415" height="105" alt="Image" src="https://github.com/user-attachments/assets/60a63af9-1938-40d1-90ea-bd51d3868b71" />

Theaxiom · 11 months ago

I disabled claude code extension in vscode and it seems to have resolved the issue.

dhitchcock · 10 months ago

This happens in Fedora 42 as well, I've tried many terminals - ptyxis, gnome-terminal, alacritty, wezterm, ghostty, rio. In Ptyxis at least I can hit Ctrl-C and clear them out, some terminals Ctrl-C and D enter junk codes too and I can't escape at all and have to just kill the terminal.

It doesn't happen right away in a fresh terminal with a fresh claude launch, but I can 100% reproduce it with certain sessions. So I've attached that session here (removed the l from jsonl cause github didn't like it)

<img width="1484" height="1019" alt="Image" src="https://github.com/user-attachments/assets/eacd36a3-dab1-4798-ad82-aa7ba3e1de31" />

572bf4e0-816b-4743-990d-73d78bf46b66.json

zach-source · 10 months ago

Terminal codes were in the tool output for me in the session file. Through some work with claude I was able to build this script that fixed it for me:

``` python
#!/usr/bin/env python3
"""
Claude Session Escape Sequence Cleaner

Removes terminal escape sequences from Claude session files that can cause
mouse tracking issues and other terminal state problems.

Usage:
claude-session-cleaner file.jsonl # Clean single file
claude-session-cleaner /path/to/sessions/ # Clean all .jsonl files in folder
claude-session-cleaner /path/to/sessions/ --recursive # Clean recursively
"""

import argparse
import os
import re
import sys
from pathlib import Path
from typing import List, Tuple

def clean_escape_sequences(text: str) -> str:
"""Remove all terminal escape sequences including mouse tracking queries."""

# All possible escape sequence patterns
patterns = [
# Standard ANSI sequences
r"\\^[\[[\d;]*m", # Color codes
r"\\^[\[[\d;]*[HJKABCDEFGPST]", # Cursor movement, clear screen, etc.
# Private mode sequences (the dangerous ones for mouse tracking)
r"\\^[\[\?[\d]+[hl]", # Private mode set/reset (like ?1049l, ?1000h)
r"\\^[\[\?[\d]+\$[p]", # Private mode queries (like ?2048$p)
# Cursor position and other query sequences
r"\\^[\[>[\d]*[a-zA-Z]", # Device status queries (like >1u)
r"\\^[\[[\d;]*[nR]", # Position reports
# Window title sequences
r"\\^[\][0-2];[^\\^[]*\\^[\\\\", # OSC sequences
# Literal escape sequences in Python strings
r"\\\\033\[[\d;]*m",
r"\\\\033\[[\d;]*[HJKABCDEFGPST]",
r"\\\\033\[\?[\d]+[hl]",
r"\\\\033\[\?[\d]+\$[p]",
r"\\\\033\[>[\d]*[a-zA-Z]",
r"\\\\033\[[\d;]*[nR]",
r"\\\\033\][0-2];[^\\\\033]*\\\\033\\\\\\\\",
# Additional color sequences that might be missed
r"\\^[\[[\d;];[\d;];[\d;];[\d;];[\d;]*m", # Extended color sequences
# Catch-all for any remaining sequences
r"\\^[\[[^a-zA-Z]*[a-zA-Z]",
r"\\\\033\[[^a-zA-Z]*[a-zA-Z]",
]

cleaned_text = text
for pattern in patterns:
cleaned_text = re.sub(pattern, "", cleaned_text)

return cleaned_text

def count_escape_sequences(text: str) -> int:
"""Count escape sequences in text."""
escape_patterns = [
r"\\^[\[[^a-zA-Z]*[a-zA-Z]",
r"\\\\033\[[^a-zA-Z]*[a-zA-Z]",
]

count = 0
for pattern in escape_patterns:
count += len(re.findall(pattern, text))
return count

def clean_file(file_path: Path, backup: bool = True) -> Tuple[int, int]:
"""
Clean a single session file.
Returns (sequences_before, sequences_after)
"""
try:
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()

before_count = count_escape_sequences(content)

if before_count == 0:
return 0, 0

# Create backup if requested
if backup:
backup_path = file_path.with_suffix(f"{file_path.suffix}.backup")
backup_path.write_text(content, encoding="utf-8")

cleaned_content = clean_escape_sequences(content)
after_count = count_escape_sequences(cleaned_content)

# Write cleaned content back
with open(file_path, "w", encoding="utf-8") as f:
f.write(cleaned_content)

return before_count, after_count

except Exception as e:
print(f"❌ Error processing {file_path}: {e}", file=sys.stderr)
return 0, 0

def find_session_files(path: Path, recursive: bool = False) -> List[Path]:
"""Find all .jsonl session files in path."""
if path.is_file():
return [path] if path.suffix == ".jsonl" else []

pattern = "**/.jsonl" if recursive else ".jsonl"
return list(path.glob(pattern))

def main():
parser = argparse.ArgumentParser(
description="Clean escape sequences from Claude session files",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__.split("Usage:")[1] if "Usage:" in __doc__ else "",
)

parser.add_argument(
"path",
type=Path,
help="File or directory path to clean"
)

parser.add_argument(
"--recursive",
"-r",
action="store_true",
help="Recursively process subdirectories"
)

parser.add_argument(
"--no-backup",
action="store_true",
help="Don't create backup files"
)

parser.add_argument(
"--dry-run",
action="store_true",
help="Show what would be cleaned without modifying files"
)

args = parser.parse_args()

if not args.path.exists():
print(f"❌ Path does not exist: {args.path}", file=sys.stderr)
sys.exit(1)

# Find session files
session_files = find_session_files(args.path, args.recursive)

if not session_files:
print(f"No .jsonl files found in {args.path}")
sys.exit(0)

print(f"🔍 Found {len(session_files)} session file(s) to process")

total_removed = 0
files_cleaned = 0

for file_path in session_files:
if args.dry_run:
try:
content = file_path.read_text(encoding="utf-8")
before_count = count_escape_sequences(content)
if before_count > 0:
print(f"📄 {file_path.name}: {before_count} escape sequences (dry run)")
files_cleaned += 1
total_removed += before_count
except Exception as e:
print(f"❌ Error reading {file_path}: {e}", file=sys.stderr)
else:
before_count, after_count = clean_file(
file_path, backup=not args.no_backup
)

if before_count > 0:
removed = before_count - after_count
print(f"✅ {file_path.name}: removed {removed} escape sequences")
files_cleaned += 1
total_removed += removed
else:
print(f"✓ {file_path.name}: already clean")

if args.dry_run:
print(f"\n🔍 Dry run complete: {total_removed} escape sequences in {files_cleaned} files")
else:
print(f"\n🎉 Cleaned {files_cleaned} files, removed {total_removed} escape sequences total")

if not args.no_backup and files_cleaned > 0:
print("💾 Backup files created with .backup extension")

if __name__ == "__main__":
main()

vehka · 7 months ago

@zach-source, thanks for sharing the python script! I can confirm it fixes a corrupted claude code session for me.

github-actions[bot] · 6 months ago

This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.

0xble · 5 months ago

Subscribing for updates. Reproducing this with Zellij + Ghostty - SGR mouse sequences like [<65;42;6M leak into input when mouse_mode is enabled in Zellij.

RobHarrisAZBlue · 5 months ago

Same happens for me in multiple terminals. Powershell, Command Prompt, Bash in and out of Windows Terminal-

<img width="1032" height="184" alt="Image" src="https://github.com/user-attachments/assets/4b7032f1-f238-4937-b20c-266c8a9cf377" />

LTe · 5 months ago

Same issue.

  1. Open Claude Code
  2. Run Mouse over the terminal
  3. You should see escape characters for mouse tracking
2.1.29 (Claude Code)
Ghosty v1.2.3
Macosx 26.2 (25C56)

<img width="1732" height="332" alt="Image" src="https://github.com/user-attachments/assets/1de36d2d-a49d-420d-94d4-4b4acd391a10" />

dbaileydev · 5 months ago

Yep same thing for me

<img width="1046" height="99" alt="Image" src="https://github.com/user-attachments/assets/d91fe8ba-216b-49d7-8c97-dfee4103712d" />

flobo3 · 3 months ago

For anyone experiencing this issue on Windows while using ConEmu (or terminals wrapping it), there is a specific fix for this environment.

The issue happens because the terminal is sending raw mouse tracking events (escape sequences) directly to the console input, and Claude Code's TUI doesn't consume them properly, causing them to spill into the prompt.

How to fix it in ConEmu:

  1. Open ConEmu Settings (Win+Alt+P or right-click the title bar -> Settings).
  2. Go to Keys & Macro -> Mouse.
  3. Uncheck the option "Send mouse events to console".
  4. Save settings and restart your terminal session.

This completely stops the [<65;42;6M style garbage characters from appearing when you move or click the mouse. Hope this helps someone!

cemercier · 1 month ago

Have this issue since yesterday, this is a real blocker I cannot use Claude Code anymore...

  • Operating System: MacOS 26.5
  • Claude CLI version: 2.1.152
  • Terminal: iTerm2 (3.6.10) and also basic mac os Terminal (2.15)
geopti · 1 month ago

the same problem returned again

cemercier · 1 month ago

I've found out I have this problem when Claude Code ends up waiting for a user input, for me it was because Claude triggered a git command that requires passphrase input. Adding the ssh key before hands solved my issue.