Windows: clipboard read/write spawns a PowerShell process per operation

Status Open
Reported on v2.1.227
Maintainer reply None cached
Activity 1 comment · opened Aug 11, 2026

What happens

On Windows, Claude Code reads and writes the clipboard by spawning a PowerShell process per operation. Observed via Win32_Process while drag-copying in the TUI — every copy spawns:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -NonInteractive -Command
  "[Console]::InputEncoding = [Text.Encoding]::UTF8; Set-Clipboard -Value ([Console]::In.ReadToEnd())"

and the read side uses the matching Get-Clipboard -Raw.

On a normal machine the spawn is cheap and this is invisible. On a corporate endpoint with several security agents (EDR / DLP / DRM, all injecting into new processes and scanning them), process creation is dramatically slower, and the spawn becomes the dominant cost of every copy and paste.

Impact

In practice the delay is 3–5 seconds per copy, and it is genuinely disruptive — long enough that you stop mid-thought and wait for the copied N chars to clipboard message before doing anything else. Pasting into the input box is affected the same way, around half a second to a second.

Timing the exact spawned command in isolation gives a median of 2024 ms (see below); the larger figure users actually experience is that plus contention with everything else the endpoint is doing, plus the redraw afterwards. Same machine, same session, reproducible on every copy. The identical setup on a personal machine is instant.

Measurements

| what | this machine | typical |
|---|---|---|
| the exact command Claude Code spawns per copy (5 runs, text piped to stdin) | 1808–2167 ms, median 2024 ms | |
| perceived end-to-end delay per copy, in normal use | 3–5 s | instant |
| powershell.exe -NoProfile -Command exit | 1672–1897 ms | |
| cmd.exe /c exit (bare process creation) | 1054–1120 ms | 10–25 ms |
| clipboard write via Win32 API, 100 → 100,000 chars | 0.6–0.8 ms | |
| clipboard read via Win32 API, 100 → 100,000 chars | 0.2 ms | |

So the spawn is roughly 2500× the cost of the clipboard call it exists to make.

The clipboard APIs themselves are fast and flat with payload size, including from a freshly compiled unsigned binary. The entire cost is the process spawn.

This was localised by elimination: it is not the terminal (a plain shell prompt in the same window pastes instantly), not the terminal emulator (reproduces in Windows Terminal and VS Code's integrated terminal), not session length or window size, not the injected security DLLs (a browser on the same machine carries a strict superset of them and is unaffected), and not statusLine (a 120 s trace over continuous copying recorded zero spawns of the interpreter and helpers that the status line command requires).

Suggested fix

Call the Win32 clipboard API directly (OpenClipboard / SetClipboardData / GetClipboardData) instead of shelling out. Measured above at sub-millisecond, versus ~1 s for the spawn on affected machines.

The binary already contains an OSC 52 path, but on Windows it is unreachable: the mode selector returns native for macos / windows / wsl unless SSH is detected (pw()?.ssh ?? !!process.env.SSH_CONNECTION). Setting SSH_CONNECTION does reach a non-native path, but it also changes other behaviour — mouse capture is released, so the app's own gutter-aware selection is lost and the terminal's plain selection takes over, which includes the left gutter. So there is currently no way to opt out of the spawn without giving up the selection behaviour.

Either of these would resolve it:

  1. Use the native clipboard API on Windows rather than a child process.
  2. Allow the OSC 52 clipboard path to be selected independently of SSH detection, without altering mouse capture.

Environment

  • Claude Code 2.1.227, native installer build (~/.local/bin/claude.exe)
  • Windows 11 Pro 26200, x64
  • Reproduces under both Windows Terminal and the VS Code integrated terminal
  • Endpoint with multiple always-on security agents; two real-time AV engines registered simultaneously

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗