Windows: select-to-copy text is lost by RDP clipboard redirection (double clipboard write races rdpclip)
Windows: select-to-copy text is lost by RDP clipboard redirection (double clipboard write races rdpclip)
Environment
- Claude Code 2.1.234, running on the RDP server machine
- Windows Server 2025, build 10.0.26100 (client machine also Windows)
- Claude Code hosted in Windows Terminal 1.24.260710001
- The machine is accessed over RDP with clipboard redirection enabled
(reproduced with an MSTSCAX-based client; the failure is generated
server-side, so any RDP client is affected)
Symptom
Selecting text in the Claude Code TUI shows "copied N chars to clipboard".
Pasting on the same machine works. Pasting on the RDP client machine
does nothing — the client's clipboard ends up with no text format at all
(Get-Clipboard returns empty). Copies from every other app on the server
(Notepad, PowerShell Set-Clipboard, clip.exe) cross the RDP boundary
fine, so users conclude RDP clipboard sharing works except for Claude Code.
Reliable user-facing repro:
- RDP into a Windows machine (clipboard redirection on).
- Run Claude Code in Windows Terminal inside that session.
- Select any transcript text → "copied N chars to clipboard".
- Ctrl+V in any app on the client machine → nothing pastes.
What actually happens (both sides instrumented)
A WM_CLIPBOARDUPDATE listener on the server recorded one select-to-copy:
17:05:38.002 owner=WindowsTerminal formats=4 CF_UNICODETEXT=yes ← write #1 (OSC 52 handled by WT)
17:05:38.288 owner=powershell formats=6 CF_UNICODETEXT=yes ← write #2 (Claude Code's spawned writer)
17:05:38.291 owner=none formats=6 CF_UNICODETEXT=yes (writer process already exited)
17:05:38.294 owner=none formats=6 CF_UNICODETEXT=yes
So one "copy" performs two clipboard writes ~200–290 ms apart:
- OSC 52 is emitted and Windows Terminal writes 4 clean text formats.
- A cold-spawned
powershellchild performs a WinForms/OLE-style write
(final state: DataObject, CF_UNICODETEXT, Ole Private Data,
CF_LOCALE, CF_TEXT, CF_OEMTEXT) in multiple clipboard
transactions, then exits (clipboard owner dies).
A no-read watcher on the client (logs sequence/owner/format count viaCountClipboardFormats/IsClipboardFormatAvailable only — deliberately
never reads data, because reading materializes the delayed offer and masks
the bug) captured the corresponding arrivals:
17:08:36.556 owner=<rdp client> formats=8 CF_UNICODETEXT=True ← offer for write #1: good
17:08:36.707 owner=<rdp client> formats=3 CF_UNICODETEXT=False ← offer for write #2: NO TEXT FORMATS
rdpclip on the server snapshots write #2's format list in the middle of
its multi-step transaction — at a moment when only[DataObject, Ole Private Data, CF_LOCALE] exist and the text formats have
not been written yet — and advertises that truncated list. The client
faithfully replaces the good offer from write #1 with a text-less offer.
Result: the client clipboard has no CF_UNICODETEXT/CF_TEXT at all, so
paste is a no-op and Get-Clipboard returns empty, even though the
server clipboard ends in a perfectly correct state (which is why the
bug is invisible to any server-side inspection after the fact).
Negative controls, all of which sync correctly to the client:
- a single atomic Win32 write (
OpenClipboard/EmptyClipboard/
SetClipboardData(CF_UNICODETEXT)/CloseClipboard);
- an identical-signature ephemeral WinForms
SetDataObject(..., true)
writer whose process exits (dead owner, DataObject marker present);
- two-writer bursts replicating the real sequence at 169/191/404/426 ms
gaps, with identical text, including a 4-format first write and writes
triggered at the exact mouse-release instant.
Only Claude Code's real writer loses the race deterministically — its
cold-spawned child's write steps are spread widely enough for rdpclip's
snapshot to land inside the transaction every time.
Suggested fix
On Windows, write the clipboard once and atomically: a singleOpenClipboard/EmptyClipboard/SetClipboardData(CF_UNICODETEXT)/CloseClipboard transaction (what clip.exe does), instead of the current
combination of (a) OSC 52 → the hosting terminal writes, plus (b) a
multi-transaction OLE/WinForms write from a spawned process. Either write
suppressed (or both replaced by one raw write) removes the mid-transaction
window that rdpclip's format-list snapshot can hit. As-is, every RDP user
who runs Claude Code on a remote machine silently loses select-to-copy
across the session boundary.
Workarounds until fixed
- Paste into any app on the server (e.g. Notepad) and re-copy from there.
- Any clipboard-history/monitor tool on the client that polls with reads
incidentally "rescues" the first offer before it is replaced.