Clipboard copy fails in Zellij and other multiplexers (OSC52 written to stdout instead of /dev/tty) #8493

Open
opened 2026-02-16 18:10:06 -05:00 by yindo · 1 comment
Owner

Originally created by @jackm-intel on GitHub (Feb 4, 2026).

Originally assigned to: @rekram1-node on GitHub.

Description

Clipboard copy functionality fails when OpenCode runs inside Zellij (and potentially other terminal multiplexers). The copy notification appears but the clipboard remains empty.

Root Cause

OpenCode writes OSC52 escape sequences to process.stdout, but when running inside Zellij:

  1. stdout goes to Zellij's PTY infrastructure
  2. Zellij intercepts OSC52 sequences for its own clipboard handling
  3. Zellij does NOT passthrough child process OSC52 to the outer terminal
  4. Result: OSC52 sequences are consumed by Zellij and never reach the actual terminal emulator

Why Other Tools Work

Tools like lazygit and neovim successfully copy to clipboard in Zellij because they write OSC52 sequences directly to /dev/tty (or $SSH_TTY in SSH sessions), which bypasses the multiplexer's PTY layer entirely.

Example from working tools:

  • lazygit config: copyToClipboardCmd: "echo {{text}} | osc copy -d /dev/tty"
  • osc tool: Opens /dev/tty directly using tcell.NewDevTtyFromDev()

Reproduction Steps

  1. Install OpenCode v1.1.50 or v1.1.51
  2. Run OpenCode inside Zellij: zellij run -- opencode
  3. Copy any text using OpenCode's copy function
  4. Notification shows "Text copied to clipboard"
  5. Attempt to paste - clipboard is empty

Expected Behavior

Text should be copied to the system clipboard, just like when OpenCode runs outside of Zellij.

Environment

  • OpenCode version: v1.1.50, v1.1.51 (broken)
  • Terminal multiplexer: Zellij (latest)
  • Terminal emulator: WezTerm (supports OSC52)
  • OS: Linux
  • Works outside Zellij: Yes
  • Works in v1.1.48: Yes (version before PR #11744)

Technical Details

Breaking change introduced in PR #11744 (merged Feb 2, 2026)

  • Commit: 3982c7d99a
  • File: packages/opencode/src/cli/cmd/tui/util/clipboard.ts
  • Function: writeOsc52()

Current implementation:

// Writes to stdout - fails in multiplexers
process.stdout.write(osc52Sequence);

Proposed fix:

// Try /dev/tty first (bypasses multiplexer), fall back to stdout
try {
  const ttyPath = process.env.SSH_TTY || '/dev/tty';
  const ttyFd = fs.openSync(ttyPath, 'w');
  fs.writeSync(ttyFd, osc52Sequence);
  fs.closeSync(ttyFd);
} catch (error) {
  // Fallback to stdout if /dev/tty unavailable
  process.stdout.write(osc52Sequence);
}

Workaround

Downgrade to v1.1.48 and disable auto-updates:

opencode upgrade v1.1.48

Add to opencode.json:

{
  "autoupdate": false
}

References

  • Zellij OSC52 passthrough behavior: Child process OSC52 is NOT passed through to outer terminal
  • Working implementation example: https://github.com/theimpostor/osc (uses /dev/tty)
  • Similar pattern in neovim: Uses /dev/tty for OSC52 clipboard operations

Impact

This affects all users running OpenCode in terminal multiplexers (Zellij, tmux, screen) where the multiplexer intercepts OSC52 sequences. This is a common workflow for developers who use multiplexers for session management.

Originally created by @jackm-intel on GitHub (Feb 4, 2026). Originally assigned to: @rekram1-node on GitHub. ## Description Clipboard copy functionality fails when OpenCode runs inside Zellij (and potentially other terminal multiplexers). The copy notification appears but the clipboard remains empty. ## Root Cause OpenCode writes OSC52 escape sequences to `process.stdout`, but when running inside Zellij: 1. stdout goes to Zellij's PTY infrastructure 2. Zellij intercepts OSC52 sequences for its own clipboard handling 3. Zellij does NOT passthrough child process OSC52 to the outer terminal 4. Result: OSC52 sequences are consumed by Zellij and never reach the actual terminal emulator ## Why Other Tools Work Tools like `lazygit` and `neovim` successfully copy to clipboard in Zellij because they write OSC52 sequences directly to `/dev/tty` (or `$SSH_TTY` in SSH sessions), which bypasses the multiplexer's PTY layer entirely. **Example from working tools:** - lazygit config: `copyToClipboardCmd: "echo {{text}} | osc copy -d /dev/tty"` - `osc` tool: Opens `/dev/tty` directly using `tcell.NewDevTtyFromDev()` ## Reproduction Steps 1. Install OpenCode v1.1.50 or v1.1.51 2. Run OpenCode inside Zellij: `zellij run -- opencode` 3. Copy any text using OpenCode's copy function 4. Notification shows "Text copied to clipboard" 5. Attempt to paste - clipboard is empty ## Expected Behavior Text should be copied to the system clipboard, just like when OpenCode runs outside of Zellij. ## Environment - **OpenCode version:** v1.1.50, v1.1.51 (broken) - **Terminal multiplexer:** Zellij (latest) - **Terminal emulator:** WezTerm (supports OSC52) - **OS:** Linux - **Works outside Zellij:** Yes - **Works in v1.1.48:** Yes (version before PR #11744) ## Technical Details **Breaking change introduced in PR #11744** (merged Feb 2, 2026) - Commit: 3982c7d99a1fc47a70c6b5436a85220992300f2b - File: `packages/opencode/src/cli/cmd/tui/util/clipboard.ts` - Function: `writeOsc52()` **Current implementation:** ```typescript // Writes to stdout - fails in multiplexers process.stdout.write(osc52Sequence); ``` **Proposed fix:** ```typescript // Try /dev/tty first (bypasses multiplexer), fall back to stdout try { const ttyPath = process.env.SSH_TTY || '/dev/tty'; const ttyFd = fs.openSync(ttyPath, 'w'); fs.writeSync(ttyFd, osc52Sequence); fs.closeSync(ttyFd); } catch (error) { // Fallback to stdout if /dev/tty unavailable process.stdout.write(osc52Sequence); } ``` ## Workaround Downgrade to v1.1.48 and disable auto-updates: ```bash opencode upgrade v1.1.48 ``` Add to `opencode.json`: ```json { "autoupdate": false } ``` ## References - Zellij OSC52 passthrough behavior: Child process OSC52 is NOT passed through to outer terminal - Working implementation example: https://github.com/theimpostor/osc (uses `/dev/tty`) - Similar pattern in neovim: Uses `/dev/tty` for OSC52 clipboard operations ## Impact This affects all users running OpenCode in terminal multiplexers (Zellij, tmux, screen) where the multiplexer intercepts OSC52 sequences. This is a common workflow for developers who use multiplexers for session management.
yindo added the opentui label 2026-02-16 18:10:06 -05:00
Author
Owner

@github-actions[bot] commented on GitHub (Feb 4, 2026):

This issue might be a duplicate of existing issues. Please check:

  • #12039: Copy to clipboard fails on MacOS when using tmux (same OSC52 + multiplexer issue)
  • #11909: OSC52 clipboard doesn't work with VS Code Remote SSH (related OSC52 failure scenario)
  • #11996: Clipboard copy broken in terminals without OSC52 support (related OSC52 implementation issue)
  • #9374: /share clipboard copy fails over SSH (OSC52 over SSH failure)
  • #9942: Copy-on-Select fails when running opencode via Tmux in Kitty terminal (tmux-specific clipboard issue)
  • #8046: Clipboard copy only works first time after upgrading to v1.1.14 (recent regression)
  • #8237: copying to clipboard doesn't work in a remote session (like a devcontainer) (remote OSC52 issue)

Feel free to ignore if none of these address your specific case.

@github-actions[bot] commented on GitHub (Feb 4, 2026): This issue might be a duplicate of existing issues. Please check: - #12039: Copy to clipboard fails on MacOS when using tmux (same OSC52 + multiplexer issue) - #11909: OSC52 clipboard doesn't work with VS Code Remote SSH (related OSC52 failure scenario) - #11996: Clipboard copy broken in terminals without OSC52 support (related OSC52 implementation issue) - #9374: /share clipboard copy fails over SSH (OSC52 over SSH failure) - #9942: Copy-on-Select fails when running opencode via Tmux in Kitty terminal (tmux-specific clipboard issue) - #8046: Clipboard copy only works first time after upgrading to v1.1.14 (recent regression) - #8237: copying to clipboard doesn't work in a remote session (like a devcontainer) (remote OSC52 issue) Feel free to ignore if none of these address your specific case.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#8493