[BUG] Ctrl-G external editor does not disable bracketed paste mode before launching terminal editors
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
What's Wrong?
When opening an external terminal editor (e.g., neovim) via Ctrl-G, Claude Code does not disable bracketed paste mode (\x1B[?2004l) before launching the editor. This causes the editor to receive raw ^[[200~ / ^[[201~ sequences when pasting, which are interpreted as keystrokes/commands instead of being recognized as bracketed paste delimiters.
Ink (the TUI framework Claude Code uses) enables bracketed paste mode on the terminal by writing \x1B[?2004h when entering raw mode. When Ctrl-G is pressed, the editor launch function (EmA in the bundled cli.js) does the following:
- Calls
suspendStdin(), which callsprocess.stdin.setRawMode(false)but does not write\x1B[?2004lto disable bracketed paste mode on the terminal. - Writes escape sequences before launching the editor:
\x1B[?1049h (alternate screen)
\x1B[?1004l (disable focus reporting)
\x1B[0m (reset attributes)
\x1B[?25h (show cursor)
\x1B[2J (clear screen)
\x1B[H (cursor home)
- Launches the editor via
execSync(editor, { stdio: "inherit" }).
Note that focus reporting (\x1B[?1004l / \x1B[?1004h) is correctly disabled/re-enabled around the editor launch, but bracketed paste mode (\x1B[?2004l / \x1B[?2004h) is not.
After the editor exits, the restore sequence is:
\x1B[?1049l (exit alternate screen)
\x1B[?1004h (re-enable focus reporting)
\x1B[?25l (hide cursor)
Again, no \x1B[?2004h to re-enable bracketed paste (because it was never disabled).
There is a similar code path for the "thinkback" player (ZV6 function) that has the same issue.
What Should Happen?
Bracketed paste mode should be disabled before launching the terminal editor and re-enabled after it exits, consistent with how focus reporting is already handled:
- process.stdout.write("\x1B[?1049h\x1B[?1004l\x1B[0m\x1B[?25h\x1B[2J\x1B[H");
+ process.stdout.write("\x1B[?1049h\x1B[?1004l\x1B[?2004l\x1B[0m\x1B[?25h\x1B[2J\x1B[H");
- process.stdout.write("\x1B[?1049l\x1B[?1004h\x1B[?25l");
+ process.stdout.write("\x1B[?1049l\x1B[?1004h\x1B[?2004h\x1B[?25l");
Error Messages/Logs
No error messages. The symptom is that when pasting inside the editor launched via Ctrl-G, the `^[[200~` and `^[[201~` bracketed paste delimiter sequences appear as literal text or are interpreted as editor commands (e.g., in neovim normal mode, `~` toggles character case, `0` moves to beginning of line, etc.) instead of being silently consumed as paste boundaries.
Steps to Reproduce
- Set
$EDITORto a terminal-based editor (e.g., nvim) - Launch Claude Code (
claude) - Press Ctrl-G to open the external editor
- In the editor, paste text from the clipboard (e.g., via terminal paste or Ctrl-Shift-V)
- Observe that
^[[200~appears as literal text or is interpreted as commands rather than being handled as a bracketed paste sequence
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.37 (Claude Code)
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Other
Additional Information
Terminal/Shell: WezTerm/zsh
This bug was discovered and the root cause analysis was performed using Claude Code itself — by having it read and trace through the minified cli.js bundle to identify the missing escape sequences. This bug report was also written with assistance from Claude Code.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗