DEV Community

Cover image for Bracketed Paste Tells Your Agent You Pasted. A Stray ESC[201~ Ends It Early
Gabriel Anhaia
Gabriel Anhaia

Posted on AI-assisted

Bracketed Paste Tells Your Agent You Pasted. A Stray ESC[201~ Ends It Early


You copy a 60-line config block from a docs page and paste it into Claude Code's prompt. It lands as one message. Nothing is sent until you press Enter yourself.

Your terminal did that. When a program turns on mode 2004, the terminal sends ESC[200~ before the pasted text and ESC[201~ after it, so the program can tell a paste from typing. The xterm control sequences reference calls it bracketed paste mode.

The catch has been discussed since at least 2013: the end marker is only bytes. If the clipboard already contains ESC[201~, a terminal that passes it through tells the program the paste is over early, and whatever follows arrives as if you typed it. In May 2025, a post to the suckless dev list reported that st still fails that test.

What mode 2004 sends, and who turns it on

The mode is older than most of the tools that rely on it. The xterm maintainer's history page dates it to xterm patch #167 in 2002, from readline work. In 2005 a patch from JOE's author, Joe Allen, built on it so his editor could switch off autoindent while text was being pasted. Readline later made it the default: Bash 5.1 and Readline 8.1, released in December 2020, say "Bracketed paste mode is enabled by default."

Coding agents depend on it for the same reason editors did. Claude Code switches the mode on for its prompt. When a maintainer closed issue #3134 in August 2026, they said Claude Code now sends ESC[?2004l on exit, including on Ctrl+C. The bug was older versions leaving the mode on, so later pastes in the shell came out wrapped in 00~ and 01~. Codex has a paste-burst detector "for terminals without bracketed paste": on some platforms, notably Windows, a paste arrives as a fast stream of key events, and the detector exists so Enter doesn't submit mid-paste.

You can watch the markers yourself. This low-level test is adapted from a 2013 suckless thread, put on one line because bash and zsh switch mode 2004 back on whenever they print a prompt, as a comment on a KDE bug notes:

printf '\e[?2004h'; cat; printf '\e[?2004l'
Enter fullscreen mode Exit fullscreen mode

Type foo, then paste bar from anywhere. According to that thread, a terminal that supports the mode shows foo^[[200~bar^[[201~. The ^[ is how the terminal echoes the ESC byte. Press Enter, then Ctrl+D, to end cat.

The bracket is only as strong as its end marker

The xterm history page is blunt about the limits. Bracketed paste is "nothing more than a clue" to the application: it doesn't say how long the paste is, and nothing stops the end marker from appearing inside the pasted text.

Browsers make that practical. On 2 November 2017, Jann Horn posted a patch to st that drops every ESC byte from pasted data while bracketed paste is on. His reason: "Browsers permit copied data to contain escape characters," so a malicious site could fake the end sequence. He noted that xterm already filtered ESC out of pastes.

The next spring it came up in public. Comments on an LWN article about terminal emulators, posted March 31, 2018, showed copied text that put ESC[201~ in front of a control character and got past bracketed paste in urxvt. A Konsole bug filed the same day asked about Konsole, and Konsole commit 0b482990, dated April 7, 2018, removes ESC from pastes in bracketed mode. The bug itself is still marked CONFIRMED with no fixed version recorded. The xterm history page also cites a 2018 kitty commit fixing code that had "performed only one replacement" of embedded end markers.

PuTTY broke it from the other side. Its advisory, linked from the CVE-2019-17068 record, says PuTTY 0.72 sometimes sent the two markers back to back, with the pasted data after them instead of between them. An app relying on the brackets then sees the paste as typing. It was fixed in 0.73. The advisory adds a fair caveat: the bug matters for apps that use the brackets to block actions, and may have no security effect for apps that treat them as a hint.

Nearly nine years after the patch, st still passes it through

The May 2025 thread, "Hardening bracketed paste mode", opens with the point that matters most: bracketed paste was meant for editors, but shells now use it to stop pasted commands from running on their own. The post linked a demo page whose second example st failed, because the clipboard carried \033[201~, and proposed rejecting or sanitizing such pastes. It pointed at VTE, GNOME's terminal library, as one that sanitizes more characters.

The replies show how unsettled the edge is. One reported that xterm turns ESC into a space and NUL into \0 in pastes, whether or not bracketed paste is on. Another argued that the only input that must be blocked is the end sequence itself, plus NUL. The archive shows no patch coming out of the thread. Today, selnotify in st's x.c writes the clipboard bytes through unchanged apart from turning newlines into carriage returns, with \033[200~ and \033[201~ around them.

Check your own terminal in a minute

Put a fake end marker on your clipboard, then paste it into the same cat line. Nothing runs: cat only echoes what it gets.

# macOS. Linux: xclip -selection clipboard, or wl-copy
printf 'one\033[201~two' | pbcopy
printf '\e[?2004h'; cat; printf '\e[?2004l'
Enter fullscreen mode Exit fullscreen mode

Paste once. If you see ^[[200~one^[[201~two^[[201~, your terminal passed the ESC byte through, and a program reading the bracket would treat two as typed. If you see one[201~two or one [201~two between the real markers, the terminal stripped or replaced the ESC (the thread above says xterm turns it into a space). Run it in every terminal you paste agent prompts into: your usual one, the one inside your editor, and anything you reach over SSH. Clear your clipboard afterwards so the marker doesn't end up somewhere else.

To see hidden ESC bytes before a paste, pbpaste | cat -v on macOS prints them as ^[. On Linux, xclip -o -selection clipboard | cat -v does the same.

Why it matters more when the prompt belongs to an agent

The classic target was the shell. The Pastejacking demo showed a page swapping your clipboard for a command with a trailing newline, so it runs "without giving the user a chance to review the command before it executes." Bracketed paste became the shell's answer to that, which is why an embedded end marker matters.

Agent prompts take far more pasted text than shells ever did: stack traces, docs, whole issue threads, commands lifted from a web page. If a bracket closes early in an agent's input box, the rest arrives as keystrokes, and a newline in that rest looks like Enter. What happens next depends on the agent. It might send half a prompt, or send text you never meant as a message. I haven't tested Claude Code or Codex against an embedded end marker, so treat that as the shape of the risk, not a finding.

Brackets only mark where pasted text starts and ends. They say nothing about what the text tells the agent to do. A paste with instructions hidden in it is a prompt-injection problem whether the brackets hold or not. If a bad command gets through, the agent's approval prompt is what stops it; the terminal never could. A few habits follow:

  • Paste commands from sources you don't trust into a plain editor buffer first, or check the clipboard with cat -v.
  • Use a terminal that strips or replaces ESC in pastes, and confirm it with the test above.
  • Keep the agent's approval mode on for anything that runs commands.

Where Hermes IDE fits

I build Hermes IDE, so weigh this with that in mind. Hermes runs Claude Code, Codex and the other agents each in a real terminal, running the agent's own interface (README). So mode 2004 reaches the agent the way it would in any terminal, and the cat test above is how you check it there too.

Two real agent sessions side by side in Hermes IDE: Codex on orders-types and Claude Code on orders-api, each in its own hermes/ worktree, with the sessions sidebar on the left
Hermes 2.3.4: Codex on orders-types after adding a RefundStatus type, and Claude Code on orders-api after a follow-up turn, each in its own hermes/ worktree, with status, context use, cost and branch in the sidebar.

One paste fix shipped there this month. Hermes 2.2.9 fixed a freeze: "Pasting a lot of text into a terminal whose program isn't reading it no longer freezes the whole window when you resize, split or close that terminal." That's a reliability fix, not a security one. I haven't checked whether the Hermes terminal filters ESC in pastes, so run the pbcopy test in it before you rely on either answer. Why a big paste into a program that isn't reading can block at all is a TTY story, covered in Paste 2,000 Characters at a Line-Mode Prompt and macOS Keeps 1,024.

Hermes is free, source-available under BSL 1.1, and runs on macOS, Windows and Linux. It is not affiliated with Nous Research, Anthropic, OpenAI or any agent vendor. Download it or read the code on GitHub.


If this was useful

Pasted text is one of the easiest ways for something you didn't write to reach an agent, and the AI Agents Pocket Guide covers the guardrails and autonomy limits that sit around an agent loop. It's one of 8 short books in Pocket Guides for Developers, on agents, RAG, prompts, observability, events, databases and system design: the whole series is here. If you run the pbcopy test in Hermes IDE and get a surprise, open an issue on GitHub.

AI Agents Pocket Guide: Patterns for Building Autonomous Systems with LLMs

Top comments (0)