Skip to content

OSC 52 clipboard copy doesn't work in tmux when allow-passthrough is off #19982

Closed as not planned

Description

@Layamon

Bug Description

OpenCode's "copy to clipboard" (drag to select → copy) doesn't work when running inside tmux with allow-passthrough off. The UI shows "copied to clipboard" but the content never reaches the local system clipboard.

Environment

  • OpenCode: v1.3.6
  • OS: Linux (remote dev machine, accessed via SSH)
  • Terminal: iTerm2 (macOS)
  • Multiplexer: tmux 3.4
  • tmux config: set-clipboard on, allow-passthrough off

Root Cause

OpenCode's writeOsc52 function detects tmux via $TMUX env var and wraps the OSC 52 sequence in a DCS passthrough:

function writeOsc52(text) {
  const base64 = Buffer.from(text).toString("base64");
  const osc52 = `\x1B]52;c;${base64}\x07`;
  const passthrough = process.env["TMUX"] || process.env["STY"];
  const sequence = passthrough ? `\x1BPtmux;\x1B${osc52}\x1B\\` : osc52;
  process.stdout.write(sequence);
}

When tmux has allow-passthrough off (the default), the DCS passthrough sequence (\x1BPtmux;...\x1B\\) is silently dropped. The OSC 52 never reaches the outer terminal.

Other tools like Neovim handle this correctly — they rely on tmux's set-clipboard on forwarding mechanism instead of DCS passthrough, so OSC 52 works regardless of the allow-passthrough setting.

Expected Behavior

OpenCode should fall back to sending the raw OSC 52 sequence when allow-passthrough is not available, letting tmux's set-clipboard on handle the forwarding (same path that works for Neovim).

Workaround

TMUX= opencode

Unsetting $TMUX makes OpenCode skip the DCS passthrough wrapping. The raw OSC 52 is then correctly intercepted and forwarded by tmux's set-clipboard on.

Suggested Fix

In writeOsc52, prefer raw OSC 52 over DCS passthrough when running in tmux. DCS passthrough should only be used for sequences that tmux's set-clipboard won't forward (like bracketed paste, etc.). For OSC 52 specifically, set-clipboard on already provides the correct forwarding behavior:

function writeOsc52(text) {
  const base64 = Buffer.from(text).toString("base64");
  const osc52 = `\x1B]52;c;${base64}\x07`;
  // Don't use DCS passthrough for OSC 52 — tmux's set-clipboard on handles forwarding
  process.stdout.write(osc52);
}

Or, if DCS passthrough is needed for other reasons, detect whether it's actually available (e.g., by querying allow-passthrough via tmux control mode) before using it.

Activity

  1. added
    opentuiThis relates to changes in v1.0, now that opencode uses opentui
    on Mar 30, 2026
  2. github-actions commented on Mar 30, 2026

    @github-actions
    Contributor

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

    If your issue is distinct (e.g., specifically about the allow-passthrough off case rather than the /dev/tty forwarding issue), please clarify how it differs from the above.

  3. kommander commented on Mar 30, 2026

    @kommander
    Contributor

    Yeah but that then doesn't work for users having allow-passthrough on. There is no way to detect what tmux options are set via ssh. It's different for every other user and environment.

  4. removed
    opentuiThis relates to changes in v1.0, now that opencode uses opentui
    on May 3, 2026
  5. NewbieToEverything commented on May 19, 2026

    @NewbieToEverything

    Additional finding: the tmux passthrough wrapper also has a double ESC byte bug

    I investigated the same issue with tmux pipe-pane hex dump capture and found an additional subtle code bug in the writeOsc52() function beyond the allow-passthrough discussion.

    The template \x1BPtmux;\x1B${osc52}\x1B\\ has an extra \x1B (ESC) byte between tmux; and ${osc52}. Since osc52 itself starts with \x1B (ESC ]52;c;...), the actual bytes sent are:

    Expected: ESC P tmux; ESC ]52;c;base64 BEL ESC \
    Actual:   ESC P tmux; ESC ESC ]52;c;base64 BEL ESC \
                              ^^
                         double ESC — NOTE the extra \x1B in the template:
                         `\x1BPtmux;\x1B${osc52}\x1B\\`
                                    ^^  this ESC is the bug
    

    Confirmed by tmux pane capture:

    Offset  Hex                  ASCII
    0518:   1b 50 74 6d 75 78 3b  ESC P t m u x ;
    0525:   1b                     ← extra ESC byte  
    0526:   1b 5d 35 32 3b 63 3b  ESC ] 5 2 ; c ; (OSC52 begins)
    

    This extra ESC can cause the terminal to reject the OSC52 sequence.

    The same template pattern appears in the GitHub source at:
    packages/opencode/src/cli/cmd/tui/util/clipboard.ts

    Fix: Change \x1BPtmux;\x1B${osc52}\x1B\\ to \x1BPtmux;${osc52}\x1B\\ (remove the \x1B before the OSC52 payload).

    Workaround: env -u TMUX opencode (unset $TMUX to skip the DCS wrapper entirely, letting tmux set-clipboard on handle raw OSC52 forwarding).

  6. github-actions commented on Jul 19, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions