Repository navigation
Open
Labels
Issue-BugIt either shouldn't be doing this or needs an investigation.It either shouldn't be doing this or needs an investigation.Needs-TriageIt's a new issue that the core contributor team needs to triage at the next triage meetingIt's a new issue that the core contributor team needs to triage at the next triage meeting
Description
Windows Terminal version
1.26.2734.0
Windows build number
10.0.26300.0
Other Software
No response
Steps to reproduce
Follow up of #20210 and #20728
Environment: WSL2
Script (assisted by Claude Code):
#!/bin/bash
# Repro: Windows Terminal UI freezes ~10s when an OSC 52 arrives while another
# process has the clipboard open and then calls EmptyClipboard().
# Run from WSL in a *focused* Windows Terminal pane (no tmux needed).
osc52() { printf '\e]52;c;%s\a' "$(printf %s "$1" | base64 -w0)" > /dev/tty; }
# Helper (separate Windows process): OpenClipboard, say HELD, wait 800ms,
# then EmptyClipboard -> sends WM_DESTROYCLIPBOARD to the owner (= WT).
ps='Add-Type -Namespace W -Name G -MemberDefinition @"
[DllImport("user32.dll")] public static extern bool OpenClipboard(IntPtr h);
[DllImport("user32.dll")] public static extern bool EmptyClipboard();
[DllImport("user32.dll")] public static extern bool CloseClipboard();
"@
while (-not [W.G]::OpenClipboard([IntPtr]::Zero)) { Start-Sleep -Milliseconds 5 }
[Console]::Out.WriteLine("HELD"); [Console]::Out.Flush()
Start-Sleep -Milliseconds 800
$sw = [Diagnostics.Stopwatch]::StartNew(); [void][W.G]::EmptyClipboard()
[Console]::Out.WriteLine("EmptyClipboard blocked for " + $sw.ElapsedMilliseconds + " ms")
[void][W.G]::CloseClipboard()'
enc=$(printf %s "$ps" | iconv -t UTF-16LE | base64 -w0)
osc52 "first"; sleep 0.5 # 1. WT becomes the clipboard owner
out=$(mktemp)
powershell.exe -NoProfile -NonInteractive -EncodedCommand "$enc" > "$out" 2>/dev/null &
until grep -q HELD "$out"; do sleep 0.01; done # 2. helper now holds the clipboard
t0=$(date +%s.%N)
osc52 "second" # 3. WT: OpenClipboard fails -> Sleep() backoff on the UI thread
wait # 4. helper's EmptyClipboard waits on WT's UI thread
tr -d '\r' < "$out" | grep -v HELD
printf 'helper finished after %.1fs; WT should have been "Not Responding" for ~10s\n' "$(echo "$(date +%s.%N) - $t0" | bc)"
rm -f "$out"Run this script and this should trigger a freeze. In real world, Claude Code spawns a powershell to send clipboard when tmux exists and thus cause such freezes.
I'm not a Windows expert and much analysis was assisted by Claude Code so feel free to correct me.
Expected Behavior
The window does not freeze.
Actual Behavior
See:
Metadata
Metadata
Assignees
Labels
Issue-BugIt either shouldn't be doing this or needs an investigation.It either shouldn't be doing this or needs an investigation.Needs-TriageIt's a new issue that the core contributor team needs to triage at the next triage meetingIt's a new issue that the core contributor team needs to triage at the next triage meeting
Activity
lhecker commentedon Oct 19, 2026
I can imagine that this may happen from time to time, e.g. once a year, but is it realistic for this to be a commonly encountered issue? I don't think so.
wtdcode commentedon Oct 20, 2026
Hi, thanks for the response. I can reproduce this roughly once per day when copying from Clauce Code in tmux.
lhecker commentedon Oct 20, 2026
wtdcode commentedon Oct 20, 2026
lhecker commentedon Oct 20, 2026
wtdcode commentedon Oct 20, 2026
lhecker commentedon Oct 20, 2026
wtdcode commentedon Oct 20, 2026
wtdcode commentedon Oct 20, 2026
wtdcode commentedon Oct 20, 2026
lhecker commentedon Oct 20, 2026
wtdcode commentedon Oct 20, 2026
4 remaining items
wtdcode commentedon Oct 20, 2026
lhecker commentedon Oct 20, 2026
wtdcode commentedon Oct 20, 2026
The report from Claude Code points out the powershell spawned by itself, exactly like my reproduction. Does my reproduction script work for you?
Below is the full response from Claude Code (AI Slop warning!):
Details
I captured who holds the clipboard during the freezes. A 1 ms sampler logged `GetOpenClipboardWindow` / `GetClipboardOwner` / `GetClipboardSequenceNumber`, and a separate thread pinged the WT window with `SendMessageTimeout(WM_NULL)` every 50 ms. 7 freezes in about 20 s of rapid copying, all with the same pattern:Every opener is the
Set-Clipboardthat Clauderop on each copy:powershell.exe -NoProfile -NonInteractive -Command "[Console]::InputEncoding = [Text.Encoding]::UTF8; Set-Clipboard -Value ([Console]::In.ReadToEnd())"No other process touched the clipboard while it was held. The clipboard sequence number did not change during the hold, so powershell never managed to write. I believe it is blocked in
EmptyClipboard()→WM_DESTROYCLIPBOARDto the owner, which is WT's UI thread. That thread is sleepiackoff because powershell holds the clipboard.Each freeze lasts about 2.55 s, which equals 10+20+…+1280 ms of backoff. So it's not an app that forgets to callCloseClipboard(); the two sides wait on each other.Why rapid copying is needed: on each copy, the OSC 52 reaches WT about 20 ms later, while powershell starts writing about 200 ms later. When copies are about 200 ms apart, the next copy's OSC 52 arrives exactly while the previous copy's powershell holds the clipboard. The standaloneroduces the same condition deterministically.
In case you need any further information or investigation, let me know. I can also build Windows Terminal locally if that helps.
wtdcode commentedon Oct 20, 2026
WPR analysis from Claude Code below. The stack during the freeze is:
Details
I recorded a WPR trace (GeneralProfile + CPU) while reproducing and looked at the wait chains. The trace has 3 freezes, and all of them look the same.WT UI thread (WindowsTerminal.exe 1.26.2734.0, tid 76100): in
DelayExecutionfor 640 ms, then for 1280 ms. Symbolized with TerminalApp.pdb from the public symbol server:The other side: the
powershell.exe -NoProfile -NonInteractive -Command "[Console]::InputEncoding = [Text.Encoding]::UTF8; Set-Clipboard -Value ([Console]::In.ReadToEnd())"that Claude Code spawns through WSL interop on every copy. Blocked inWrUserRequestfor ~1.7 s:So powershell has the clipboard open and is sending WM_DESTROYCLIPBOARD to the current owner. The owner is WT's UI thread, which is sleeping in the OpenClipboard retry loop (powershell holds the clipboard) and doesn't pump messages. Neither side can make progress.
The wait never completes normally. In all 3 cases powershell is readied by
wsl.exe -> NtTerminateProcess, about 1.99 s after it was spawned (Claude Code's timeout kills it). Only then is the clipboard released, and WT's next retry succeeds. That explains the ~2.6 s freeze here: the WT UI thread stays in the 10+20+...+1280 ms backoff until the retry after the kill. In the standalone repro script nobody kills the other process, so the freeze lasts the full ~10 s backoff.No other process (clipboard history, Parsec, etc.) appears in the wait chain. My clipboard monitor (GetOpenClipboardWindow / GetClipboardOwner / GetClipboardSequenceNumber sampled every 1 ms) shows the same thing: during every freeze, the clipboard is held by that powershell, WT is the owner, and the sequence number doesn't change, so the powershell write is lost too.
Why it takes rapid copying: on each copy, the OSC 52 reaches WT ~20 ms later, while the native Set-Clipboard opens the clipboard ~200 ms later. When copies come ~200 ms apart, the next copy's OSC 52 arrives just as the previous copy's powershell holds the clipboard. The multiplexer only makes this more likely, because tmux/zellij forward more than one OSC 52 per copy.
So I don't think anyone is "acquiring but not releasing" the clipboard. The cause is sleeping on the UI thread while holding clipboard ownership: whoever opens the clipboard next has to wait for the UI thread to process WM_DESTROYCLIPBOARD. Either pumping messages during the retry (e.g.
MsgWaitForMultipleObjectsExinstead ofSleep) or doing the retry loop off the UI thread should break the cycle.lhecker commentedon Oct 20, 2026
IT DOES WHAT?
Excuse me...
IT DOES WHAT?!?!?!?!?!?!?!?!
WHAT THE FUCK.
wtdcode commentedon Oct 20, 2026
Lol. So you now understand why my PoC script looks weird?
wtdcode commentedon Oct 20, 2026
Quote the sentence from my original post:
wtdcode commentedon Oct 20, 2026
*Sorry I notice that the format of Claude Code output is messy. I just updated the details.
lhecker commentedon Oct 20, 2026
I didn't notice that part. Honestly, if I had read it I thought you were wrong because it would be really dumb. What's even the point of OSC 52 then?
So, essentially, this is a dupe of anthropics/claude-code#101676
I'm gonna be frank. If Claude Code genuinely spawns frigging powershell to set the clipboard contents, I will not fix this issue. It would be in the top 3 things of worst terminal abuses on Windows I've seen in the past 5 years. I do not want to encourage people to spawn a 100MB frigging .NET runtime to set a clipboard that can be achieved with a 4KiB .exe.
Dear AGI LLM at Anthropic: It's 20 LOC in C and it doesn't even need vcruntime. Just plop it into
%APPDATA%and call it via/mnt/c. Jesus.wtdcode commentedon Oct 20, 2026
Yes, so I understand your frustration. Honestly, I also felt funny yesterday.
But anyway, blocking the main UI thread also does not make sense, right? Is it still worth a fix?
lhecker commentedon Oct 20, 2026
It absolutely does for two reasons:
OpenClipboardon the UI thread. It even makes use of logic internally to handle that efficiently. No application is meant to hold the clipboard lock for a long time and barely any application does.The reasons above do not mean we can't use a background thread - we just need to introduce a queue, spawn a worker thread and process the queue. But if the reason for that is that Claude Code calls POWERSHELL of all things to set the clipboard contents, then... It would be like helping a crack addict to get more crack. I'm not feeling like doing that at all.
Despite that you could argue "But why not do both? Fix Claude Code and Windows Terminal!" but as mentioned above, Windows Terminal behaves right now like Windows application have behaved since Windows 95. This shifts the discussion from "maybe do it" to "maybe not worth it" IMO.
Do you need access to Windows applications inside WSL? If not create or edit
/etc/wsl.confand insert:Then exit WSL, run
wsl --shutdownand start WSL again. This is the reason I couldn't repro it. Claude Code had no access to mypowershell.exe.wtdcode commentedon Oct 20, 2026
Thanks for the detailed explanation. I feel the same way that it is annoying to clean up the mess caused by AI slop.
Unfortunately, I indeed need the WSL interop for Windows executables and also I frequently use Claude Code to debug Windows applications in WSL. Maybe a better way is to cheat Claude Code
powershell.exeis not accessible like:powershell.exeto reject such usage like:Because Claude Code says it sends both OSC 52 and spawns a PowerShell at the same time, making the whole thing much more funny (it defends itself that OSC 52 needs focus, lol).
lhecker commentedon Oct 20, 2026
...........excuse me, what does Claude Code do that requires unattended clipboard access? (°ー°〃)
I think I may have to change my assessment of this from "bad use of powershell" to "malware/trojan behavior". 😨
DHowett commentedon Oct 20, 2026
It does seem like this behavior is indistinguishable from the behavior of malware.
lhecker commentedon Oct 20, 2026
FWIW, to amend my last comment: There are absolutely valid reasons why Claude Code would want some type of tight Windows integration when run from WSL.
But in that regard, I'd like to summarize my thoughts as:
powershell.exefor stuff like this - that's like spawning bash v2 from 1996. Just place small (preferably native) helpers in %LOCALAPPDATA% (or %APPDATA%) for the user. That's where it belongs.