Repository navigation
Description
Environment
- opencode 2.2.0, TUI, Linux (app runs inside a Docker sandbox)
- Host terminal: GNOME Terminal (VTE-based). VTE does not implement OSC 52 — long-standing open feature request, open since 2017: https://bugzilla.gnome.org/show_bug.cgi?id=795774
WAYLAND_DISPLAYinside the sandbox points to a minimal compositor stub
What happens
Mouse-selection copy in the TUI shows the "Copied to clipboard" toast, but the text never reaches the host clipboard. Pasting later returns whatever was in the clipboard before the copy — in our case a stale literal undefined string from an earlier broken copy, which made the failure very confusing to debug.
Root cause
packages/tui/src/clipboard.ts, write() (as of v2.2.0 sources):
const result = await clipboard.writeText(text.replaceAll("\0", ""), {
destination: "all-available",
selection: "clipboard",
})
if (result.host.status === "written" || result.terminal.status === "attempted") returnWith destination: "all-available", OpenTUI writes via the host backend and emits OSC 52 to the terminal. In this environment the host write never fires (verified: zero Wayland protocol traffic from the TUI on copy attempts — no set_selection / data-control activity, observed in the compositor stub's log), so the only delivery channel is OSC 52.
terminal.status is "attempted" — per OpenTUI's own docs, "OSC 52 attempted does not confirm the terminal accepted it". That status is treated as success: the promise resolves and util/selection.ts shows the toast:
clipboard
.write(clipboardText)
.then(() => toast.show({ message: "Copied to clipboard", variant: "info" }))
.catch(toast.error)VTE-based terminals (GNOME Terminal, XFCE terminal, Tilix, Terminator — the defaults on most Linux desktops) silently ignore OSC 52, so nothing is copied while the user is told it was.
This is not limited to sandbox setups: the same lying toast occurs in any SSH/TUI session where the host clipboard path is unavailable and the local terminal lacks OSC 52 support — a very common combination.
Expected behavior
- Treat OSC 52
"attempted"as unverified, not success. OpenTUI already has an OSC 52 capability query (isOsc52Supported(), XTGETTCAP-style). When the reported capability isunknown/unsupportedand the host write did not succeed, the feedback should reflect that (e.g. "Copied — delivery not confirmed", or an error), not an unqualified success toast. - (Secondary) The native Wayland host backend appears to enter a terminal failed/unsupported state after the initial connect and never retries subsequent writes — worth a look for robustness.
Verification
Marker test: selected a unique string that had never been in any clipboard; the "Copied" toast appeared; wl-paste on the host machine returned the previous clipboard content, not the marker. Repeated with the same result.
Activity
opencode-agent commentedon Oct 27, 2026
Thanks for the detailed report. I reproduced this on 2.2.0, and it still happens on the latest
dev(96ae220).What I did: ran the TUI on Linux with no host clipboard (no
DISPLAY, noWAYLAND_DISPLAY, no clipboard helpers), in a virtual terminal that ignores OSC 52, like VTE. Then I drag-selected some text on the home screen. The recording and screenshot below show the "Copied to clipboard" toast, even though nothing received the text.In the same environment, OpenTUI's host clipboard returns
unsupported, so the only thing sent was the OSC 52 sequence, with statusattempted. As you pointed out,write()inpackages/tui/src/clipboard.tscounts that as success, so the success toast is shown. The script below reproduces it.I did not test your second point, that the Wayland host backend never retries after it first fails, because that needs a compositor. Related: #50208 (the same false toast over SSH + tmux) and #59052 (the same
all-availableclipboard setting).recording.mp4
Reproduction script (drive.ts)
Run it with opencode-drive:
opencode-drive run ./drive.ts. It drivesopencodefrom yourPATH; setOC_DEVto the path of an OpenCode checkout to run that from source instead.MostDef2000 commentedon Oct 28, 2026
Follow-up: root cause was on our side — native Wayland copy works; the toast issue stands
Update after deeper debugging (opencode 2.2.0, same environment). The "host write never fires" observation in the original report was an artifact of our minimal compositor stub, not an OpenTUI/opencode behavior. Two bugs on our side, both fixed:
Blocking backup channel in the compositor stub. The stub ran a blocking
urlopen(timeout=6)inline in its single-threaded event loop after each delivery. Any gateway response slower than OpenTUI's 1s host-clipboard timeout (DEFAULT_CLIPBOARD_TIMEOUT_MS = 1_000,host-clipboard.internal.ts) wedged the client backend, whose failed phase is terminal — it never wrote again. The first two copies in a session worked; everything after a slow response died silently.Missing
cancelledevent on selection replacement. Our stub did not send the protocol-mandatedcancelledto the previous data source when a selection was replaced. OpenTUI's provider pool (wayland.zig:canPublishProvideradmits a new provider only while < 2 un-cancelled providers exist per selection type;retireProviderfrees a slot only aftercancelledAND in-flight transfers completed) then exhausts after exactly two copies — the third copy fails withselectionFailure(.provider)before any network traffic. This part is our protocol non-conformance, not an OpenTUI bug; real compositors sendcancelled.After fixing the stub, mouse-copy → host clipboard works reliably: 5+ consecutive copies verified in the compositor log, each correctly cancelling the previous, plus interleaved external
wl-copyclients.What remains valid upstream feedback (the core of this issue):
packages/tui/src/clipboard.tswrite()resolves success whenterminal.status === "attempted"even ifhost.statusis"failed"/skipped — during the wedged period above, every copy showed "Copied to clipboard" while nothing was delivered anywhere (OSC 52 into a VTE terminal goes nowhere). Suggestion: treat a failed host write as a failure (or surface a warning toast) instead of masking it with the terminal "attempted" status.Happy to provide the stub logs or a minimal non-conformant-compositor reproducer if useful.