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[?2004h (enable) \x1b[?2004l (disable)\033[?2004h / \033[?2004l\e[?2004h / \e[?2004lESC [ ? 2 0 0 4 h / l1b 5b 3f 32 30 30 34 68 / 6cDescription
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
printf '\033[?2004h' # enable\nprintf '\033[?2004l' # disableimport sys; sys.stdout.write('\x1b[?2004h')fmt.Print("\x1b[?2004h")process.stdout.write('\x1b[?2004h')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. Runprintf '\033[?2004l'to disable, or open a new terminal. See thestuck-bracketed-pastepitfall. - 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
\ntriggers 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 againstsudo.
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
| xterm | Linux console (fbcon) | macOS Terminal.app | iTerm2 | Windows Terminal | cmd.exe / ConPTY | kitty | alacritty | WezTerm | Ghostty | GNOME Terminal | Konsole | tmux | GNU screen |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| yes | no | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes |