Skip to main content
ansicode

DECSET ?2004 — Bracketed paste mode

Wrap pasted text in distinct escape markers so apps can tell paste from typing.

Byte forms

Every common string-literal form so you can paste-and-search either direction.

\\x1b[\x1b[?2004h (enable) \x1b[?2004l (disable)
\\033[\033[?2004h / \033[?2004l
\\e[\e[?2004h / \e[?2004l
ESC [ESC [ ? 2 0 0 4 h / l
hex1b 5b 3f 32 30 30 34 68 / 6c

Description

Documented in xterm-ctlseqs as DEC Private Mode 2004 (added to xterm patch #203 in 2005 and now the universal default). The enable byte sequence is \x1b[?2004h and the disable is \x1b[?2004l — both are standard DEC private-mode toggles, distinguished from regular CSI parameters by the leading ?. When enabled, the *terminal* (not the application) wraps every clipboard paste it delivers to the foreground program with \x1b[200~ immediately before the pasted bytes and \x1b[201~ immediately after. The application can then distinguish "user typed this" from "user pasted this" — critical for shells (so paste does not trigger keyboard shortcuts / autoindent / history-expansion mid-paste) and for editors (so paste is treated as one undo unit rather than per-keystroke). The markers themselves are an xterm extension: ECMA-48 doesn't define them, and no other escape uses the ~ final byte in this position.

Support is broad on GUI emulators (xterm, iTerm2, kitty, alacritty, wezterm, gnome-terminal, Konsole, Ghostty, Windows Terminal, modern ConPTY 1809+ via Win32 console-mode bit ENABLE_VIRTUAL_TERMINAL_INPUT) and absent on Linux console / fbcon (the kernel pty has no clipboard concept). The most-reported user-facing bug is the stuck-bracketed-paste pitfall: a TUI emits ?2004h on startup but crashes / kills before sending ?2004l on exit, leaving the terminal in bracketed-paste mode and the user's plain-shell paste arriving as literal ^[[200~ls -la^[[201~ strings on the prompt line. Recovery: printf '\x1b[?2004l' or reset. bash 5.1+ and zsh 5.8+ readline both *do* understand the markers and will silently consume them when correctly bound — bind -p | grep paste checks the binding; older shells or shells without the readline binding leak the markers through. Tmux passes the escape through and adds its own paste-bracketing layer on top (tmux 3.2+); GNU screen 4.x / 5.0 also forwards bracketed paste correctly. The escape pair has no terminfo cap — feature-detection is by terminal-version probe (\x1b[>q DA2) or by trial.

The matching close \x1b[?2004l is mandatory before exit. The robust pattern is to pair the enable with the close in the same atexit / signal-handler block that handles other terminal-mode cleanup (rmkx for application keypad, cursor restore, mouse disable, alt-screen leave). The more durable approach is the XTSAVE / XTRESTORE stack (\x1b[?2004s to push the prior state, \x1b[?2004r to pop) which leaves the terminal in whatever state the parent shell wanted — important because parent shells with their own bracketed-paste binding will turn the mode back on after your child exits if you unconditionally turned it off, but an unbalanced XTSAVE / XTRESTORE causes confusion in the other direction. For paste *security* — preventing paste-injection attacks where a user inadvertently pastes attacker-supplied shell commands — bracketed-paste is the protocol layer; the shell binding then has to choose to either insert (zsh / bash 5.1+ default) or refuse-and-prompt (more conservative). Related: alt-screen (DECSET ?1049 — usually entered alongside ?2004h on TUI startup), cursor-visibility (?25 — same cleanup block), dec-focus-events (?1004 — same cleanup block).

Spec citation: xterm-ctlseqs (Private mode 2004)

Examples

bash
printf '\033[?2004h'  # enable\nprintf '\033[?2004l'  # disable
python
import sys; sys.stdout.write('\x1b[?2004h')
go
fmt.Print("\x1b[?2004h")
javascript
process.stdout.write('\x1b[?2004h')
c
printf("\x1b[?2004h");

Used in

Real-world tools that emit this sequence — anchors the bytes to commands you've already used.

  • bash 4.4+ readlineenables `?2004h` so pasted text is delivered as one chunk and not interpreted as keypresses (Enter, etc.)
  • zsh`bracketed-paste-magic` ZLE widget — disables expansion + history on the paste
  • vim, neovim`xterm-bracketed-paste` autodetect — paste enters insert mode automatically without triggering indent / abbreviations
  • fish — bracketed-paste binding shipped by default
  • psql, sqlite3 readline-backed CLIsstops a pasted SQL block from auto-running on every embedded newline

Frequently asked

Short answers to the questions developers actually search for this sequence.

Why is [200~ printed in my terminal when I paste?
Your shell or TUI enabled bracketed paste (\x1b[?2004h) but exited without disabling it (\x1b[?2004l). The terminal is still wrapping every paste in \x1b[200~\x1b[201~ markers, but nothing is consuming them — so they land in your prompt as literal text. Run printf '\033[?2004l' to disable, or open a new terminal. See the stuck-bracketed-paste pitfall.
Why do I need bracketed paste at all?
Without it, a multi-line paste into a shell is indistinguishable from the user typing each line — every \n triggers immediate command execution, so pasting a code snippet runs every intermediate line as a command. Bracketed paste tells the shell 'this is a paste, not typing' so it can buffer the whole block, defer Enter / history evaluation, and protect against paste-injection attacks against sudo.

Terminal support

xterm
yes
Linux console (fbcon)
no
macOS Terminal.app
yes
iTerm2
yes
Windows Terminal
yes
cmd.exe / ConPTY
yes
kitty
yes
alacritty
yes
WezTerm
yes
Ghostty
yes
GNOME Terminal
yes
Konsole
yes
tmux
yes
GNU screen
yes

Related sequences

In the family cookbook

DEC cookbook · 4. Bracketed paste — `\x1b[?2004h`