Repository navigation
Description
Summary
On opencode 2.1.0, starting the TUI over SSH can leave the tail of the terminal's OSC 11 color reply in the prompt as :ffff/ffff/ffff. The TUI sends OSC 10/11 color probes at startup; when the reply is fragmented (here: ESC ]11;rgb arrives first, the rest arrives about 2s later), the incomplete prefix is discarded and the late tail is treated as typed input. It is cosmetic, but it pollutes the first input and can be submitted accidentally.
Environment
- opencode version: 2.1.0
- OS: Ubuntu 24.04.5 LTS, Linux 6.8.0-147-generic x86_64
- Terminal: Windows Terminal (PowerShell 7) over SSH; remote
TERM=xterm-256color,TERM_PROGRAM/COLORTERM/VTE_VERSIONunset - Shell: remote
/bin/bash; local PowerShell 7 - Install/channel: release standalone binary at
~/.opencode/bin/opencode, latest channel - Active plugins: none found
- Multiplexer: none (
$TMUXunset)
Reproduction
Real-world:
- SSH into a Linux host from a terminal that delivers OSC 10/11 replies in fragments.
- Run
opencode2.1.0. - Before typing anything, the prompt contains
:ffff/ffff/ffff(sometimes the preceding]10;rgb:...]11;rgb:...is visible too). It reproduces on every startup.
Deterministic local PTY reproduction:
- Start
opencode2.1.0 under a PTY. - Wait until the
ESC ]11;?query is written. - Write
ESC ]10;rgb:2e2e/3434/3434 BELandESC ]11;rgb(stop in the middle of the OSC 11 reply). - Wait 2 seconds, then write
:ffff/ffff/ffff BEL. - The rendered prompt contains exactly
:ffff/ffff/ffff.
Control results with the same harness:
- Whole reply in one write: no leak.
- Split at
ESC ]11;rgbwith a 50 ms gap: no leak. - Split at
ESC ]11;rgbwith a 2 s gap: leaks the exact string.
This points to the escape-sequence timeout in the stdin parser discarding the incomplete OSC prefix, after which the late tail is parsed as ordinary text.
Expected Behavior
Terminal query replies, including fragmented or delayed ones, should be consumed as terminal responses and must never appear in the prompt. Late bytes belonging to an already-abandoned reply should be drained/discarded.
Actual Behavior
The tail of the reply is rendered into the prompt. It is stable on every startup on the affected SSH terminal. It is not persisted in the session database, so it appears to be a startup input-parser race.
Additional Context
- PTY capture of 2.1.0 startup shows unconditional
ESC ]10;?,ESC ]11;?, palette and capability queries. - No environment/config switch tested stops the OSC 10/11 probes:
COLORFGBG,NO_COLOR,TERM=dumb, pinning the theme, andOTUI_USE_ALTERNATE_SCREEN=falseall still send them. - Potential workaround: run opencode under tmux so the outer terminal's fragmented reply does not reach the TUI directly.
Ctrl+Uclears the leaked line. - Related reports (same family, some auto-closed): tmux: terminal's replies to startup capability probes printed at the prompt #40035 (tmux startup replies printed at the prompt), Terminal escape sequences printed on startup in tmux #16547, tui: stale terminal reply bytes leak into shell input after exit (paste shows "8;6u" garbage) #54500, tui: Kitty graphics probe (Gi=31337) leaks into the shell prompt after exiting V2 #55641.
Minimal PTY reproduction script
#!/usr/bin/env python3
import os, pty, select, signal, struct, time, fcntl, termios
pid, fd = pty.fork()
if pid == 0:
os.execvpe("opencode", ["opencode"], os.environ)
fcntl.ioctl(fd, termios.TIOCSWINSZ, struct.pack("HHHH", 40, 120, 0, 0))
os.set_blocking(fd, False)
out = bytearray()
sent_prefix = False
sent_tail = False
start = time.time()
while time.time() < start + 6:
r, _, _ = select.select([fd], [], [], 0.05)
if r:
try:
out += os.read(fd, 65536)
except OSError:
break
if not sent_prefix and b"]11;?" in out:
sent_prefix = True
# First fragment: complete OSC 10 reply, then only the prefix of OSC 11
os.write(fd, b"\x1b]10;rgb:2e2e/3434/3434\x07\x1b]11;rgb")
deadline = time.time() + 2.0
if sent_prefix and not sent_tail and time.time() >= deadline:
sent_tail = True
# Late tail of the OSC 11 reply; this is what leaks into the prompt
os.write(fd, b":ffff/ffff/ffff\x07")
time.sleep(1)
os.kill(pid, signal.SIGTERM)The rendered output then contains the literal string :ffff/ffff/ffff.
Activity
opencode-agent commentedon Oct 20, 2026
Thanks for the detailed report and the PTY script. I was able to reproduce this.
On
@opencode/[email protected]I ran the TUI under a PTY and replied to the startupESC ]11;?query in two parts:ESC ]11;rgb.:ffff/ffff/ffff BEL.The late tail showed up in the prompt every time. With a 50 ms gap nothing leaked. The same thing happens on the latest
dev(d30e990) run from source. The screenshot below shows thedevTUI with:ffff/ffff/ffffin the prompt, and the script below reproduces it (python3 repro.py 2; pass0.05for the control run).There is no video because our TUI automation tool can't send raw terminal reply bytes, so I used a real PTY instead.
The cause looks like what you describe: the stdin parser's escape timeout drops the unfinished OSC 11 prefix, and the rest is then treated as typed text. Related reports, but not the same problem: #55641 and #54500 (probe replies leaking after exit), #40035 and #16547 (probe replies printed under tmux).
Reproduction script (repro.py)
Run it:
python3 repro.pysyhhyl commentedon Oct 29, 2026
Still reproduces on 2.2.0 (Linux, standalone binary). Same bug, but the fragmentation point is different from the report: here it splits right after the lone
ESC, not in the middle of the payload.Environment
~/.opencode/bin/opencode, latest channel)TERM=xterm-ghostty); insidetmux 3.4it also reproduces (TERM=xterm-256color, tmuxccolourenabled)#eeeeeeSymptom
The prompt is pre-filled with
]10;rgb:eeee/eeee/eeee(usually seen as:eeee/eeee/eeee, since the leading]10;rgbis easy to miss). Cosmetic, but one accidental Enter sends it. Nothing was ever submitted here:~/.local/state/opencode/prompt-history.jsonlhas no such entry.Deterministic reproduction (2.2.0)
opencodeunder a PTY (or in a tmux pane)ESCbyte]10;rgb:eeee/eeee/eeeeRendered prompt afterwards:
Control results with the same harness:
ESC \terminated: consumed correctly, no leakESC+ rest with a 10 ms gap: no leakESC+ rest with a 30 ms gap: leaksCause
In the bundled
StdinParser, a pending loneESCis flushed as the Escape key aftertimeoutMs = 21and setsjustFlushedEsc. The ground-state recovery then only re-syncs on[(0x5B):So any terminal reply whose continuation arrives after a >20 ms pause and starts with
](OSC),P(DCS),_or^(APC/PM) is typed as keys instead of being consumed. The prefix is dropped because the incomplete escape sequence was already discarded, which is why only the tail...:eeee/eeee/eeeeis visible.Status of the fix
anomalyco/opentui#1793("native: hold split terminal replies while a DA1 sentinel is out", merged 2026-10-26) andanomalyco/opentui#1816("core: end terminal reply waits at their DA1 reply", merged 2026-10-27) describe exactly this failure mode and fix it. The 2.2.0 binary does not contain them —OT_INPUT_EXPECT_SENTINELis absent from the bundled parser. So this should go away with the next OpenTUI bump; can this stay open until then?flexiphp commentedon Oct 29, 2026
Possibly related: I see similar stray escape-sequence text in the prompt on Windows, with a different string.
Environment
opencode --version)What I see
38;2;91;73;124m(the leading ESC[ is missing).Why I think it may be related
I have not confirmed whether the OpenTUI PRs mentioned above (#1793, #1816) change this behavior; I will retest once a release containing them is available. Happy to provide more details or test a build.
niven823 commentedon Oct 30, 2026
I'm seeing the same issue on v2.2.2. In my case, the text that appears in the prompt is :eeee/eeee/eeee.
Anthony14FR commentedon Nov 12, 2026
I'd like to help with this, together with #57517 and #60268, which look like the same root cause: the stdin parser's escape timeout drops an unfinished sequence and the tail is typed into the prompt. That parser lives in
@opentui/core, not in this repo. Would you rather have the fix in OpenTUI (keep a pending OSC/CSI sequence open longer when it arrives split), or a change on the OpenCode side? I'll follow whichever you prefer.