Skip to main content
ansicode

OSC 9 — Toast notification (iTerm2 / Windows Terminal)

Trigger a native OS desktop notification from the terminal — long-running job done, build finished, etc.

Byte forms

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

\\x1b[\x1b]9;MESSAGE\x07
\\033[\033]9;MESSAGE\007
\\e[\e]9;MESSAGE\a
ESC [ESC ] 9 ; MESSAGE BEL
hex1b 5d 39 3b ... 07

Description

Raise a system-tray / Notification Center / KNotifications toast with the given message string. Originally iTerm2's iTerm2 growl mechanism (the OSC 9 alias dates to the Growl daemon era on macOS); Windows Terminal adopted the same control later. The terminal forwards the payload to the host OS notification API — the user sees a banner outside the terminal window, useful for long compiles, make && say done substitutes, CI pollers. Unsupported terminals drop the entire OSC 9 frame silently, so it is safe to emit unconditionally. Caveat: ConEmu repurposes OSC 9 as its progress-bar protocol (\x1b]9;4;<state>;<percent>\x07) — a different scheme — so check your terminal docs before sending complex OSC 9 payloads.

Spec citation: iTerm2 Proprietary Escape Codes (OSC 9) / Windows Terminal

Examples

bash
make && printf '\033]9;build finished\007'
python
import sys; sys.stdout.write('\x1b]9;build finished\x07')
go
fmt.Print("\x1b]9;build finished\x07")
javascript
process.stdout.write('\x1b]9;build finished\x07')
c
printf("\x1b]9;build finished\x07");

Used in

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

  • make + iTerm2 build-done toast`make all && printf '\033]9;build finished\007' || printf '\033]9;BUILD FAILED\007'` pops a Notification Center banner the moment a long compile lands — the developer can context-switch away from the terminal and still see the result on a glance at the macOS top-right
  • WezTerm + Windows Terminal CI watcherslong-running CI pollers (`gh run watch`, `circleci-cli local execute --tail`) emit OSC 9 on state transition — both WezTerm and Windows Terminal forward to the Windows Action Center / Linux notify-osd, so a developer babysitting a pipeline gets a system-level alert without an extra `notify-send` install
  • tmux pane-activity hookswith `set -g activity-action other` plus a tiny wrapper, tmux turns its built-in `monitor-activity` into a real OS toast: the wrapper emits `\x1b]9;activity in <pane>\x07` on every activity event, so a multi-pane work session can flag the user even when no terminal is focused
  • Ghostty + Konsole tail-watching scripts`tail -F deploy.log | grep --line-buffered ERROR | while read -r l; do printf '\033]9;%s\007' "$l"; done` raises a Ghostty / KDE Plasma notification per error — better than waking on every status line because the toast queue is rate-limited by the OS rather than the terminal
  • ConEmu progress-bar variant (OSC 9;4 sub-protocol)ConEmu hijacks OSC 9 with a sub-syntax `\x1b]9;4;<state>;<percent>\x07` to drive a Windows-7-style taskbar progress bar — `state=1` normal, `2` error, `3` indeterminate. Modern shells (PowerShell `oh-my-posh`, Bash `bash-completion`) emit it during long file operations to surface progress without printing a literal text bar

Frequently asked

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

Why does my OSC 9 notification show in iTerm2 but get printed literally in Windows Terminal?
OSC 9 is not a single standard — three terminals adopted the same Ps=9 slot for *different* purposes. iTerm2 uses \x1b]9;<message>\x07 for system notification banners (calls into macOS Notification Center). ConEmu / Windows Terminal use \x1b]9;<state>;<value>\x07 for taskbar progress (states 0–4: clear / normal / error / indeterminate / pause). xterm treats OSC 9 as undefined and prints the body. To get cross-terminal notifications, pick the right escape per terminal: \x1b]9;text\x07 for iTerm2, \x1b]777;notify;Title;Body\x07 for urxvt / KDE, \x1b]99;i=<id>:p=body:;<text>\x1b\\ for kitty 0.31+. There is no single universal notification escape.
How do I detect which OSC 9 dialect the current terminal speaks?
There's no DSR / DECRQM probe for OSC 9 semantics — the byte slot is shared, the meaning isn't. Use XTVERSION \x1b[>q to get the terminal name, then dispatch: name matches iTerm2 → notification dialect; matches ConEmu / WT_SESSION env var set → progress dialect; matches kitty → use OSC 99 (the modern desktop-notification spec); fall through to notify-send / terminal-notifier for unknown terminals. Don't try to probe by emitting a test OSC 9 — terminals that print it literally will leave garbage in your scrollback even if you wrap the test in alt-screen.

Terminal support

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

Related sequences

In the family cookbook

OSC cookbook · 6. Inline images & progress — `OSC 1337` and `OSC 9 ; 4`