macOS: clipboard copy silently lost in su'd (non-Aqua) local sessions — writes to a background pasteboard
Summary
On macOS, when Claude Code runs in a shell that isn't attached to the GUI login (Aqua) session — most commonly after su - <otheruser> in a local Terminal — copying out of Claude Code silently fails. Both the fullscreen-TUI ctrl+c copy and /copy shell out to pbcopy, which targets the pasteboard of the calling session's bootstrap namespace. In a non-Aqua session that's a per-uid background pasteboard that no GUI app ever reads, so nothing lands on the clipboard the user is actually looking at. pbcopy exits 0, so there is no error — the copy just vanishes. This is especially painful because a su'd session is exactly where Claude hands you commands to paste somewhere else.
Environment
- macOS
- Local Terminal.app,
su - <otheruser>(no SSH involved) - Fullscreen TUI (
"tui": "fullscreen")
Repro
- In Terminal.app as GUI user A, run
su - userB. - Start Claude Code, select text, copy with
ctrl+c(or run/copy). - As user A, switch to any GUI app and paste → nothing (or stale content) arrives.
Confirming signals inside the su'd shell:
launchctl managernameprintsBackground, notAqua.echo x | pbcopy && pbpasteround-trips within the session, but the GUI clipboard is untouched — proof the write went to a different, background pasteboard.
Root cause (hypothesis)
Clipboard-mode selection appears to treat only SSH_CONNECTION (plus container heuristics) as "my clipboard is not the user's clipboard," then falls through to the native (pbcopy) path on macOS. But the property that actually matters is whether the process shares the user's GUI session, not whether it's remote. A su'd local session is not remote, yet it is still detached from the Aqua pasteboard, so the native path writes into the void.
Suggested fix
On macOS, gate the native clipboard path on GUI-session membership, not just !SSH_CONNECTION. The bootstrap namespace answers it directly: treat a non-Aqua launchctl managername (or SessionGetInfo / SessionHasGraphicAccess reporting no graphic access) as "not the user's clipboard," and fall back to OSC 52 the same way an SSH session already does.
Related: the recovery hint is unreachable on this exact path
There is already a helpful fallback hint — "hold <modifier> while selecting for native copy" (Fn in Terminal.app, Option in iTerm2). But it's emitted only on the OSC 52 code path. On macOS-without-SSH the mode is native, which never reaches that branch, so the one hint that would rescue this case is never shown on the only platform where users hit it. Emitting the modifier hint on the macOS native path (at least when the destination session may be non-GUI) would close the loop.
Workaround for anyone hitting this now
In Terminal.app, hold Fn while drag-selecting (Option in iTerm2) to suppress mouse reporting, then ⌘C. The terminal — running as the real GUI user — makes a native selection and performs the copy itself, so the su boundary is never crossed. Zero config. (OSC-52-capable terminals like iTerm2/Ghostty/WezTerm/kitty avoid the whole problem; Terminal.app does not support OSC 52.)