[BUG] Paste causes permanent session freeze — input tokenizer consumes PASTE_END inside unterminated OSC sequences
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
⚠️ Note on duplicates: The symptom (paste freeze) has been reported before (#11270, #13183, #15733), but those were closed without identifying the cause. This issue provides the root cause and a concrete fix. The related open issues (#3134, #5017) are different bugs (exit cleanup and truncation, respectively).
What's Wrong?
Pasting text that contains unterminated ANSI escape sequences (OSC, DCS, or APC — common when copying from terminals with hyperlinks or colored output) causes a permanent session freeze. No input is accepted, Ctrl+C doesn't work. Requires kill -9.
Root cause: The input tokenizer's OSC/DCS/APC states only exit on BEL or ESC+ST. When pasted content has an unterminated OSC (e.g. \x1b]8;;url without BEL), the tokenizer stays in OSC state and consumes the PASTE_END marker (\x1b[201~) as OSC body — because ESC [ ≠ ESC \. The keypress parser never sees PASTE_END, stays in IN_PASTE mode forever, and all subsequent input (including Ctrl+C) is silently swallowed. No recovery timeout exists for this case.
What Should Happen?
Paste should complete normally. If pasted content contains malformed escape sequences, they should be treated as text — not cause the parser to lose track of the paste boundary.
Error Messages/Logs
No error messages — the session silently freezes. The only visible symptom is that [201~ sometimes appears in the prompt (the raw PASTE_END marker leaking through).
Steps to Reproduce
- Open iTerm2 (or any terminal with bracketed paste support)
- Run
claude - Copy text from a source that includes unterminated OSC sequences — for example:
- Terminal output with OSC 8 hyperlinks (
ls --hyperlink=auto) - Any text containing
\x1b]without a BEL (\x07) or ST (\x1b\) terminator
- Paste into the Claude Code prompt
- Session freezes permanently — no keyboard input accepted
Minimal reproduction using printf:
# Copy this to clipboard (contains unterminated OSC):
printf '\x1b]8;;https://example.com some table content' | pbcopy
# Then paste into Claude Code — session will freeze
Proof of concept (tokenizer behavior):
const PASTE_START = '\x1b[200~'
const PASTE_END = '\x1b[201~'
// ✅ Clean text — works
// Tokenizer parses PASTE_END as CSI sequence → parser exits paste mode
// ❌ Text with unterminated OSC — freeze
// Input: PASTE_START + '\x1b]8;;https://example.com table' + PASTE_END
// 1. Tokenizer sees \x1b] → enters OSC state
// 2. Looks for BEL or ESC+ST to exit — never finds them
// 3. \x1b[201~ (PASTE_END) is consumed as OSC body (\x1b[ ≠ \x1b\)
// 4. Parser stays in IN_PASTE forever → all input swallowed → FROZEN
Is this a regression?
No, this never worked
Claude Code Version
2.1.87
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
iTerm2
Additional Information
Suggested fix: In the tokenizer's OSC/DCS/APC state handlers, recognize ESC [ as a new CSI sequence starting — close the unterminated sequence and transition to parsing the CSI normally:
// Add before the fallback `else { i++ }` in OSC/DCS/APC cases:
} else if (
code === C0.ESC &&
i + 1 < data.length &&
data.charCodeAt(i + 1) === ESC_TYPE.CSI
) {
// Unterminated OSC/DCS/APC — a new CSI (e.g. PASTE_END) is starting.
// Emit the incomplete sequence and parse the CSI normally.
emitSequence(data.slice(seqStart, i))
seqStart = i
result.state = 'escape'
i++
}
~12 lines, single file change. Does not break properly terminated sequences.
Related issues:
- #11270 — Paste freeze (closed as dup of #3134, but different root cause)
- #13183 — Paste freeze with voice-to-text tools (closed as stale)
- #15733 — Paste freeze with Unicode characters (closed)
- #3134 — Bracketed paste not disabled on exit (different bug, open)
- #5017 — Paste content truncated on Windows (different bug, open)
This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗