[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
- ask claude to execute something for a python source code in a pipenv based project
- when it comes out with
python -m xxxxxxxask 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
18 Comments
<img width="895" alt="Image" src="https://github.com/user-attachments/assets/9694d78f-c6cf-42d0-acf5-eeca56104081" />
issue persists
I also observed this in the newest CC. I observe the following behavior:
I have the same issue after CC have executed some python code
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:
``
bash
`reset
`or
bash
``tput reset
``
bash
``printf '\033[?1000l'
``
bash
``tmux refresh-client -c
Ctrl+Shift+Deleteto reset the terminalkitty @ send-text --match=active '\x1b[?1000l'To prevent this in the future:
~/.bashrcor~/.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.
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" />
I disabled claude code extension in vscode and it seems to have resolved the issue.
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
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()
@zach-source, thanks for sharing the python script! I can confirm it fixes a corrupted claude code session for me.
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.
Subscribing for updates. Reproducing this with Zellij + Ghostty - SGR mouse sequences like
[<65;42;6Mleak into input when mouse_mode is enabled in Zellij.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" />
Same issue.
<img width="1732" height="332" alt="Image" src="https://github.com/user-attachments/assets/1de36d2d-a49d-420d-94d4-4b4acd391a10" />
Yep same thing for me
<img width="1046" height="99" alt="Image" src="https://github.com/user-attachments/assets/d91fe8ba-216b-49d7-8c97-dfee4103712d" />
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:
Win+Alt+Por right-click the title bar -> Settings).This completely stops the
[<65;42;6Mstyle garbage characters from appearing when you move or click the mouse. Hope this helps someone!Have this issue since yesterday, this is a real blocker I cannot use Claude Code anymore...
the same problem returned again
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.