Windows: Console window flashing when executing tools
Status Open
Reported on v2.0.74
Maintainer reply None cached
Workaround ✓ Mentioned in thread ↓
Activity 63 comments · opened Dec 20, 2025
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
## Description
When using Claude Code on Windows (Windows Terminal), a console window briefly flashes/appears every time a tool is executed (Bash, Grep, Glob, Read, etc.).
## Environment
- OS: Windows
- Terminal: Windows Terminal
- Platform: win32
## Steps to Reproduce
- Run Claude Code on Windows
- Execute any command or let Claude use any tool
- Observe brief window flash
## Expected Behavior
Tools should execute without visible window flashing.
## Possible Solution
When creating child processes on Windows, use CREATE_NO_WINDOW flag or STARTUPINFO.dwFlags = STARTF_USESHOWWINDOW with wShowWindow = SW_HIDE.
## Workaround
Currently using WSL as a workaround.
---
What Should Happen?
11
Error Messages/Logs
Steps to Reproduce
11
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
2.0.74
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
_No response_
63 Comments
I'm also experiencing this issue, and it extends beyond just tool execution.
Additional Context
The console window flashing also occurs when plugin hooks execute. For example, the
claude-memplugin registers hooks forSessionStart,UserPromptSubmit,PostToolUse, andStopevents that runnodeandbuncommands. Each execution creates a visible console window.What I tried (unsuccessfully)
WScript.Shell.RunwithvbHideflag. Failed due to quote escaping issues withcmd /c.powershell -WindowStyle Hidden -Command "...". The PowerShell window was hidden, but the spawnednode.exe/bun.exeprocesses still created their own console windows.Root Cause
On Windows, console applications (like
node.exe,bun.exe) create console windows by default when spawned. The only reliable way to prevent this is to use theCREATE_NO_WINDOWflag when callingCreateProcess().Environment
Request
Please consider implementing
CREATE_NO_WINDOWfor all child process spawning on Windows, including:This would significantly improve the Windows user experience.
I've recently started to experience this on windows and it's a really bad user experience. You can't do anything else while claude is running or it'll get in the way
yes , it's very annoying
Hook execution also affected - critical UX impact for plugin users
User Experience Impact:
I'm experiencing this same issue with plugin hooks, not just tool execution. Every time a claude-mem plugin hook executes (
SessionStart,UserPromptSubmit,PostToolUse,Stop), a Node.js console window flashes on screen, often stealing keyboard focus mid-sentence. This happens dozens of times per session and significantly disrupts workflow.Root Cause:
The issue is in Claude Code's hook execution function (appears to be
GE1()in the bundled CLI). The spawn call is missing thewindowsHideoption:Current Code:
Required Fix:
Precedent:
This exact fix was already implemented in:
claude-agent-sdk-typescript(Issue anthropics/anthropic-sdk-typescript#103)Why Workarounds Don't Work:
All attempted workarounds fail because:
-WindowStyle Hidden→cmd.exespawns first before PowerShellstart /b→ Still creates initialcmd.exewindowThe ONLY solution is fixing the spawn call at the source.
Impact Scope:
This affects:
Business Impact:
For professional Windows users, this makes Claude Code nearly unusable. The constant window flashing:
Request:
Could this be prioritized for the next patch release? The fix is a one-line change that would immediately improve UX for all Windows users. I'm happy to test a beta build if that would help.
Environment:
Thank you for considering this!
Hi team — adding an update since my duplicate report (#16880) was closed.
I can still reproduce the same “console window flash” on Windows when tools run, now on the VS Code extension as well:
The worst part is that the flashing window steals foreground focus while it appears. During that moment I can’t interact with VS Code (keyboard input gets interrupted / clicks can be lost), so it effectively blocks other work. This is not just distracting — it actively breaks workflow.
Could you share the current status and when you expect a fix (e.g., a target version or timeframe)?
Thanks.
This is extraordinarily disruptive when using CC on Win11. One workaround is to use an admin terminal but doing so creates its own downstream issues and shouldn't be necessary. This is seriously impacting workflow - desperate for a fix.
Update: Another workaround for those finding this really annoying; commands run by agents don't appear to trigger the flashing cmd window issue (at least for me on win11, Terminal, CC CLI, non-admin account), so if you can delegate a task via agents I recommend doing so.
FYI I was plagued by this issue (it was immensely annoying) and finally today I got it fixed, this did the trick (note I have my git installed via scoop):
Omg, please fix this - its pretty much impossible to use Claude Code in a windows env with this popup.
I may need to try @DamianReeves workaround.
I also suffer from this issue, and it makes Claude Code nearly unusable. I may give up usage of it entirely, or always run it on a 2nd desktop.
I experience this through CMD line Claude, as well Claude through VSCode
I had no luck attempting @DamianReeves 's workaround
This is so annoying. Constantly having terminal windows flashing up when I'm working is a pain. Can we please have a fix for this?
Same issue here. Using Claude Desktop (not CLI) on Windows 11 with working directory on WSL (\\wsl.localhost\Ubuntu\home\...).
Every message triggers a visible /usr/bin/bash --login -i -c cygpath -u \\wsl.localhost\... window for git repo detection. The window flashes briefly and steals focus, making it very disruptive.
Attempted workarounds (none worked):
Setting SHELL to cmd.exe in ~/.claude/settings.json
Setting SHELL to powershell.exe
Removing SHELL env entirely
The windowsHide: true fix on child_process.spawn() would resolve this.
Environment:
Windows 11
Claude Desktop (latest)
Git for Windows installed (C:\Program Files\Git\cmd\git.exe)
Working directory: WSL Ubuntu via \\wsl.localhost\
Also experiencing the same issue here, rendering Claude Code incredibly tedious to use. Using Claude Code CLI (native installer) on Windows 11
Every Bash tool execution spawns a visible Git Bash window (/usr/bin/bash --login -i -c) that flashes open and closed.
The window steals focus mid-typing, making the experience very disruptive.
Attempted workarounds (none worked):
The windowsHide: true fix on child_process.spawn() would resolve this.
Environment:
Regression: not present in 2.1.42, confirmed in 2.1.47
This issue appears to be a regression introduced between v2.1.42 and v2.1.47. On v2.1.42, no CMD window flashing or focus stealing occurs during tool execution. On v2.1.47, every tool call spawns a visible console window that steals keyboard focus.
Timing aligns with the new "session environment" / shell snapshotting mechanism introduced in v2.1.45, which also caused other Windows regressions (#26481, #26610, #26746). The new shell mechanism likely changed how child processes are spawned, dropping the
windowsHideflag or spawning via a different code path that doesn't suppress console windows.Environment:
This issue appears to have been fixed, at least for me (Win 11, CC CLI, v2.1.50 but v2.1.47 also worked) - CC is saying it can see windowsHide: true" in the bash shell spawn and I'm not getting flashing cmd windows on tool usage on my non-admin user account. Can't see anything in the changelog, but great its addressed.
Edit: worth noting I'm still on the original npm version of CC CLI, not the new "native" install approach, in case that makes a difference.
Linking this here: https://github.com/anthropics/claude-code/issues/28138#issuecomment-3959696822
Is this going to get addressed? I am stuck on 2.1.42 which seems to be the last working version before this bug was introduced!
I dug into it and here is what I can tell based on the available info to the public:
Windows: Console window flashing regression introduced in v2.1.45
Since v2.1.45, every tool call, hook execution, and MCP server startup on Windows spawns a visible
cmd.exe/conhost.exewindow that flashes on screen and steals keyboard focus. v2.1.42 is the last version without this behavior.This has been reported independently by many users across 14+ issues over the past two months with no official response. Posting this as a consolidated comment with root cause analysis to hopefully get some traction.
What happens
When Claude Code executes any tool (Bash, Grep, Glob, Read, etc.), runs a hook, or starts an MCP server, a black console window briefly appears and steals keyboard focus. If you're typing in another application, keystrokes get swallowed. If you have hooks running on every prompt or tool call, the flashing is nearly continuous. It makes Claude Code effectively unusable on Windows without pinning to v2.1.42.
Root cause analysis
The regression aligns precisely with the introduction of the session environment / shell snapshotting mechanism in v2.1.45. Here's the evidence:
windowsHide: truetochild_process.spawn()callswindowsHide: truefix from v2.1.27"Session environment not yet supported on Windows"(#26610) confirms the feature shipped without full Windows supportonecmdleak in$SHELLOPTS(#26481) shows the snapshotting mechanism usesbash -o onecmdto capture environment, and this flag leaks into user sessionswindowsHide: trueIS set in 12 places in the binary, but the ConPTY/SEA binary context causesconhost.exeallocation anyway — the new spawn paths introduced by shell snapshotting likely bypass or override these settingsAdditional changes in v2.1.45 that may contribute:
v2.1.47 made things worse with "Fixed hooks silently failing on Windows by using Git Bash instead of cmd.exe" (#25981), which created yet another spawn path. That same version also introduced hook path mangling on Windows (#26746) where backslash separators get stripped.
Related issues
Primary reports:
Duplicate/related reports:
-iflag in cygpath call (closed as dup)Same-root-cause regressions from v2.1.45:
onecmdin SHELLOPTS)Workarounds
| Workaround | Effectiveness |
|------------|--------------|
| Pin to v2.1.42 +
DISABLE_AUTOUPDATER=1| Fully works, but stuck on old version || Custom COMSPEC wrapper (details in #19012) | Fully works, requires compiling a C++ GUI-subsystem exe |
| Set
CLAUDE_CODE_GIT_BASH_PATHtoC:\Program Files\Git\bin\bash.exe| Works for some users || Use WSL instead of native Windows | Works but changes the whole workflow |
| Disable plugin hooks (claude-mem, etc.) | Reduces frequency but doesn't eliminate |
What a fix probably looks like
Based on community investigation, the fix needs to ensure
windowsHide: true(or the Win32CREATE_NO_WINDOWflag) is set on allchild_process.spawn()andchild_process.exec()calls — including the ones introduced by the shell snapshotting mechanism in v2.1.45 and the hook execution path changed in v2.1.47. The MCP SDK also has a bug wherewindowsHideis conditional on"type" in processwhich only returnstruein Electron, so it's effectively alwaysfalsein Node.js (#24708).Environment
<img width="2286" height="1042" alt="cmd-exe-hydra.png — cut off one window, two more shall take its place" src="https://github.com/user-attachments/assets/fa5e4e9b-8c2b-4c9b-b5c8-5e0cfc5fd4aa" />
Tell me you don't use windows to develop claude code without saying you don't use windows!
Just try opening the start menu or typing while this is going on....
Update: Narrowed down the hook-specific flashing on v2.1.59 (npm). Internal tool calls do not flash with hooks disabled — the regression appears to be specifically on the hook execution spawn path where
windowsHide/CREATE_NO_WINDOWwas lost in v2.1.45+.Any hook command that spawns a separate Windows console-subsystem binary (like
uv.exeoruvw.exe) triggers a visibleconhost.exewindow. Commands that run inside the existing bash process (echo,python) do not flash. This is consistent with v2.1.42 working — the hooks calleduv runon both versions, but v2.1.42 suppressed the console allocation.Workaround: use bare
pythoninstead ofuv runin hook commands.Full testing details: #28138 comment.
A fix is available as a Claude Code plugin: powershell-default
Install:
Adds a native Pwsh tool (shows as
Pwsh(...)in the UI). Commands use PowerShell syntax directly. When enabled, Bash tool is blocked. Works on any OS with PowerShell 7+.PR: https://github.com/anthropics/claude-code/pull/35761
Additional data: Procmon analysis reveals burst spawns on session start
Environment: Windows 11 Enterprise 10.0.26200, Claude Code 2.1.78, Claude Desktop
Finding: The problem is not just the 10s polling — it's burst spawns
Using Process Monitor (Sysinternals), I captured all
Process Createevents fromClaude.exe. Two distinct patterns:1. Background polling (always present, minor annoyance)
Claude.exe(PID 29504) runsgit status --porcelainevery 10 seconds. Each spawnsgit.exe→conhost.exe(-ForceV1) →git.exe(mingw64). This is 3 processes × 6/min = ~18 console windows per minute.2. Session/dialog start burst (the real problem — ~20 windows at once)
When a new session or conversation starts, Claude fires a burst of 10-15 processes within ~500ms:
Then seconds later:
And from a child Claude.exe (PID 40412):
5×
python --versionchecks in 260ms!Summary
| Pattern | Frequency | Processes per burst | Visual impact |
|---------|-----------|-------------------|---------------|
|
git statuspolling | Every 10s | 3 (git→conhost→git) | 1 flash || Session start | On new dialog | ~15-20 | ~20 simultaneous flashes |
| Plugin checks | On session start | 5-7 | 5-7 flashes |
| Python version probe | On session start | 5 | 5 flashes |
Important: disabling all hooks does NOT fix this
I disabled all project hooks (SessionStart, PreToolUse, PostToolUse) and all plugin hooks (hookify, context-mode, security-guidance). The flashing persists — it's caused by Claude Code's own internal subprocess spawns, not by user hooks.
Suggested priorities
spawn()/execFile()calls for git, gh, python, and claude.exe subprocesses needwindowsHide: true(orCREATE_NO_WINDOW)python --versionprobe running 5 times is likely a bug — should be cached after first checkStill experiencing this on v2.1.87 (Windows 11, Git Bash shell).
Impact: Every hook execution (PreToolUse, PostToolUse, Stop, SessionStart) spawns
node.exeviachild_processwithoutwindowsHide: true, causing a visible terminal flash. MCP stdio servers usingnpxalso flash at session start.What we've done to mitigate (but can't fully fix):
Readfrom PreToolUse matcher (~50% fewer hook invocations)What's still flashing: Every remaining hook spawn (2 per Edit/Write/Bash, 1 per Stop, 1 per SessionStart) and MCP stdio server connections at boot.
The fix we need:
windowsHide: trueon allchild_process.spawn()calls for hooks and MCP stdio servers on Windows. This is a one-line fix per spawn call.Reproducing this on Windows 11 Pro (10.0.26200) with Claude Code in VS Code. Git Bash windows flash on every tool call (Bash, Grep, Glob, etc.). Using the VS Code extension, not standalone CLI. Very disruptive during normal use.
This is extremely frustrating, makes using claude so much more time consuming. More often than not the console windows close themselves, but if you have a custom statusline configured this one will stay open usually, and sometimes seems to prevent commands from finishing.
Hey want to crash my laptop? Why don't you introduce a bug that creates 150+ terminal windows? Great.
Is Anthropic going to fix it? No.
Can I use a non-claude application? No because the twerps running this gong show are worried about overuse.
They're going to keep vibe coding their lil dumb application. I hope this bubble pops.
This ended up being a issue with using git-bash.exe being used for SHELL, changed in
~/.claude/settings.jsonand now no issueSeeing a more severe variant of this on Claude Code 2.1.68 / Windows 11
Enterprise 10.0.26200 with Git for Windows bash (
$SHELL=/bin/bash.exe).The Git Bash (MINGW64) window does not just flash — it opens and
stays open with an empty interactive prompt, and Claude Code is blocked
from continuing until I manually close the window. It happens intermittently
during normal Bash tool calls; no specific command reliably triggers it.
No user-configured hooks in
settings.json. Confirmed these do not fix it:git config --global core.pager catgit config --global credential.helper managerHypothesis: on Windows the Bash tool is occasionally spawning
git-bash.exe/mintty.exewith a visible console attached instead ofhidden. Spawning child processes with
CREATE_NO_WINDOW(or equivalentdetached mode) should prevent any visible terminal from ever surfacing.
If I understand correctly, we have to wait until somebody from Anthropic vibe coding team started to use windows
Still seeing the flash after setting
SHELLandCLAUDE_CODE_GIT_BASH_PATHin~/.claude/settings.json. Running Git Bash (mintty) on Windows 11, Claude Code v2.1.x.Architectural framing + open-source workaround (5-month flash-source cascade analysis)
Confirming this is still active on 2.1.145 (Windows 11 26200). I've spent ~3 months reverse-engineering the spawn topology and shipping a complete workaround stack. Sharing the architecture in case it helps prioritize the upstream fix.
Root cause is broader than
windowsHide:trueThe hook dispatch pipeline wraps EVERY hook command in
bash -c "..."even when the target IS an.exe. Example from settings.json:This gets executed as
bash.exe -c "$HOME/.claude/hooks/bin/my-hook.exe --arg"→bash.exeis CUI subsystem →conhost.exeflash, even ifmy-hook.exeis itself GUI subsystem. The bash wrapper is the visible window source for hook commands, NOT the hook executable.Proposed fix (architectural, not
windowsHide:flag tweak):When the hook command's argv[0] resolves to an
.exedirectly (no shell metachars in argv), invoke it viachild_process.spawn(<exe>, [<args>], { windowsHide: true, ... })directly, bypassing the bash wrapper. This eliminates the bash.exe → conhost.exe flash entirely for native-binary hooks.For Bash tool calls themselves (genuine shell scripts),
windowsHide:trueon the bash spawn AND on all child spawns (taskkill cleanup, ps inspection) would address the residual flashes — see related closed dups #43844, #59040, and #19012.Open-source workaround stack we deployed
Working solution shipping CUI-flash from ~155/min to ~33/min:
bash.exetrampoline — wrapsbash.exeinvocations. Detects single-token shell-metachar-free commands and bypasses bash entirely viaexec.Command(target_exe, args...). Falls back to ConPTY for legitimate shell commands. Critical fix:HOME=""(empty string set by claude.exe in 163/164 invocations observed) requires fallback toUSERPROFILE— otherwise~/<path>tokens don't expand and bypass silently fails.syscall.SysProcAttr{HideWindow: true}on theexec.Command—STARTF_USESHOWWINDOW + SW_HIDEensuresconhost.exeis allocated but never shown. Output flows normally via inherited stdin/stdout/stderr handles.~/.claude/shims/: GUI-subsystem trampolines forgit.exe,cmd.exeetc., prepended to PATH for sub-processes spawned by hook handlers. Catches the cascading flash from sub-tools that internally invoke other CUI binaries.Result on a 6-repo VS Code workspace + active hook dispatch: 0 visible conhost windows observed under sustained Bash tool load + statusline polling.
Cofactors NOT addressed by user-side workarounds
Two
claude.exe(node) internal spawn patterns bypass any user-side trampoline because they invokecmd.exevia absoluteC:\WINDOWS\system32\cmd.exepath:cmd.exe /d /s /c "taskkill /pid <X> /T /F"— 14 flash/min (Bash tool subprocess cleanup)cmd.exe /d /s /c "ps -axo user,pid,stat,command"— 5.6 flash/min (process inspection, UNIX command on cmd.exe = always fails but flash happens first)Combined ~20 flash/min purely from
claude.exeinternal subprocess inspection/cleanup that no user-side shim can intercept. These would benefit from:taskkill: replace with native Win32TerminateJobObject(no cmd.exe)ps -axo: replace withNtQuerySystemInformationor NodeprocessAPIs (no cmd.exe needed; the cmd.exe call ALWAYS fails becausepsis UNIX-only — so the data isn't even being collected)Asks
.exe direct-invokearchitectural change has higher ROI thanwindowsHidebecause it eliminates the wrapper bash process entirely (lower latency too: ~10ms/hook saved per skippedbash -cstartup × hooks-per-turn = noticeable on long sessions).New reproduction path: clicking "New Session" in Claude Desktop triggers a ~50-spawn cascade with ~30 visible conhosts
Adding evidence for a different trigger than the statusline/hook scenarios already documented: the "New Session" button itself.
Repro
What's happening (process snipe, 60ms WMI polling)
Captured the full cascade from a single click. ~50 subprocesses spawn, parent = Claude Desktop main (Electron) process. Timeline of key events after click:
| Δt from click | Subprocess | conhost |
|---|---|---|
| t+0s |
git fetch --prune origin| VISIBLE || t+0s |
git credential-manager get+store| inherited || t+5s |
git index-pack --stdin --fix-thin| inherited || t+16s |
git maintenance run --auto(auto-triggered by fetch) | inherited || t+17–27s |
git repack -d -l --geometric=2 --write-midx,git pack-objects,git multi-pack-index write— multi-second console ops | inherited || t+18s |
gh pr view <branch>,gh pr checks <num>,gh pr view <num>(PR context lookup) | VISIBLE ×3 || t+30s |
git update-ref refs/heads/main(fast-forward local main) | VISIBLE || t+32s |
git worktree add --no-checkout -b claude/<auto-name> <user-path>\.claude\worktrees\<auto-name>| VISIBLE || t+32s |
git checkout HEAD -- . :(exclude).claude,git config extensions.worktreeConfig true| VISIBLE ×2 || t+36s | Same
gh pr view <branch>as t+18s (redundant query) | VISIBLE || t+40s | New
claude.exe(claude-code child) spawns for the session | VISIBLE || t+41s | Session init:
git --no-optional-locks status --short,log --oneline -n 5,config user.name,config user.email| VISIBLE ×4 || t+41s |
cmd.exe /d /s /c "git config --get user.email"(raw cmd.exe wrapper) | VISIBLE || t+41s |
cmd.exe /d /s /c "tasklist \| findstr /I \"Code.exe Cursor.exe Windsurf.exe idea64.exe pycharm64.exe ...\""— IDE detection | VISIBLE || t+41s |
powershell.exe -NoProfile -Command "(Get-CimInstance Win32_Process -Filter ...).CreationDate.Ticks"| VISIBLE |Smoking gun: the spawn code knows how to do
--headless, just doesn't use it consistentlyOver the same 90-second window, the SAME parent (Claude Desktop main process) spawned ~30 subprocesses with auto-allocated console (no
--headlessflag — Windows wroteconhost.exe 0x4) and exactly 1 with properconhost.exe --headless --width 80 --height 24 --signal 0xNNNN --server 0xNNNN.So the bug isn't "Electron can't hide console windows on Windows" — it's that the spawn path for git/gh/cmd/powershell tool subprocesses is missing
windowsHide: true(orCREATE_NO_WINDOW), while the spawn path for some Electron-internal utility processes correctly sets it.Compounding factors observed in the same investigation
git maintenance run --autois the worst offender —git repack+git pack-objectsare seconds-long console processes. A typical click-spawn flash lasts ~50ms; these last seconds.gh pr view <branch>runs twice during a single new-session init (visible at t+18s and t+36s in the table). Halving these would halve the gh-related flashes.bash.exeprocesses (one alive 13 hours) that survived the originating claude-code session's death — generating ~8 gh.exe spawns/sec via a model-generateduntil ... do true; donebusy-wait loop. Each spawn flashed because of this issue (#14828). Without the visible-conhost bug, the orphan loop would silently waste CPU; with it, the loop becomes a focus-stealing keystroke killer.Environment
1.9255.2.0(Microsoft Store install)2.1.149Independent confirmation on Claude Code 2.1.142 / Windows 11 (build 26200).
@JustinCMR's diagnosis matches what I see — the tool-subprocess spawn path is missing
windowsHide: true/CREATE_NO_WINDOW, and the windowless path already exists in the codebase: a live process snapshot showsconhost.exe --headless --width 80 --height 24 --server 0x...being used for some child processes but not the shell/tool ones. So this is a one-flag inconsistency, not a platform limitation.One spawn path I haven't seen called out yet: MCP stdio servers. Each configured MCP server is launched as a child
node.exe/python.exeat session start/restore, and those spawns also lackwindowsHide— so they flash too. For anyone running several MCP servers, that's an extra burst of windows on every session start, on top of the per-tool-call flashes. Whatever fixes the tool-subprocess path should cover the MCP stdio launch path as well (samechild_process.spawnoptions).Mitigation for the portion that's user-owned (not the harness): if some of your flashes come from your own Windows automations (scheduled tasks, file watchers, helper scripts) rather than Claude Code itself, that part is fixable today:
-WindowStyle Hiddento apowershell.exeaction, or switch a consolepython.exeaction topythonw.exe.wscriptVBS shim —CreateObject("WScript.Shell").Run cmd, 0, False— which gives zero flash (window style 0), stronger than-WindowStyle Hidden(which can still flash briefly).I verified this silences recurring scheduled automations completely. It does not touch the Claude Code tool/MCP flashes (those still need the
windowsHidefix above), but it removed a meaningful chunk of the noise in my case.+1 for prioritizing the one-flag fix — this is a daily, focus-stealing problem on Windows.
Same root cause, confirming with an independent diagnostic method — also affects Windows PowerShell tool calls, not just Bash. Full write-up and a feature suggestion posted on #58606 (which has the closest match in scope), copying the key part here since this issue is older:
Environment: Windows 11 Home, build 10.0.26200,
anthropic.claude-codev2.1.181-win32-x64, Git Bash + Windows PowerShell 5.1.I confirmed the
bash.exe/powershell.exe→conhost.exe(noCREATE_NO_WINDOW) pattern via Windows native Process Creation auditing (Event ID 4688 in the Security log) while debugging an unrelated, separate console-flash issue on the same machine. That gave a reliable way to attribute flashes specifically to Claude Code's tool execution versus anything else running on the box.Suggestion: rather than only hiding the window, consider exposing it as a setting (
hidden/visible/visible-signedwith a distinguishing title) — some users may want the visibility as a "Claude is working" signal rather than have it fully suppressed. Details on #58606.This very annoying
claude code made an "visible-signed with a distinguishing title windows" for my powershell process -
so now there is no more flash on my computer - its very comfortable one ..
Can confirm - traced this with Windows Process Creation auditing (Event ID 4688): every Bash/PowerShell tool call from Claude Code spawns bash.exe/powershell.exe -> conhost.exe without CREATE_NO_WINDOW. Seems to be the extension host launching child processes without the no-window flag, not something fixable from the user side (settings.json hooks etc. don't touch this path). Hoping for an exposed window-style option on the tool execution side.
For anyone else dealing with console flashes from their own elevated PowerShell calls (separate from the tool-execution flash above) - a small trick that helped distinguish "my own elevated action" from "something else is flashing":
The problem
Start-Process ... -Verb RunAsopens a real console window for the elevated command. By default it opens and closes very fast (a flash) and gives no clue about who launched it.The fix: title + visible banner + wait
$host.UI.RawUI.WindowTitle = '...'renames the title bar - visible on Alt+Tab / taskbarWrite-Host '=== ... ===' -ForegroundColor Cyanprints a colored banner right when the console opens, readable even on a quick screenshot-Waitkeeps the window open for the whole duration of the action instead of a few-millisecond round tripResult: a flash with a UAC prompt + a clearly signed window is unambiguous (a deliberate elevated action), versus a flash without UAC and without a signature, which is worth investigating as coming from something else entirely (in my case, two unrelated background services that never trigger UAC turned out to be the real source of separate, unexplained flashes).
Still reproduces on v2.1.195 (Windows 11, Git Bash backend, VS Code integrated terminal).
Confirmed via process monitor — the flash is
claude.exespawning the hook/statusLine subprocess without a hidden-window flag, so Windows allocates aconhost.exeper spawn. Representative chain:Two distinct triggers on this build:
bash → jq → bunxre-spawned on every render (continuous flicker even at idle). Same as the older #51867.type:commandhook is wrapped in an un-hiddenbash -c, firing on every PreToolUse/PostToolUse/UserPromptSubmit. Plugin hooks that usebash/python3(e.g. PostToolUse-on-every-tool-call) flash on each tool use.windowsHide: truein hook config andCREATE_NO_WINDOWin the env block are both ignored — the flash isclaude.exe's own outer wrapper, before the hook command runs, so neither has any effect (matches #27115).Mitigations that actually reduce it (not eliminate): disabling hook-heavy plugins and clearing the custom statusLine. Only WSL gets it to zero. A
CREATE_NO_WINDOW/STARTF_USESHOWWINDOW+SW_HIDEflag on the internal spawn is the real fix.Install RTK (https://github.com/rtk-ai/rtk) has solved this annoying shit!! At least for Bash tool, Search still flash that cmd window
Same issue here, and it makes Claude Code very hard to use for non-stop daily work on Windows 11 (v2.1.197, PowerShell host).
Extra detail from monitoring process creation events (Win32_Process __InstanceCreationEvent) that may help pinpoint it. Visible conhost/console windows flash on EVERY turn from Claude Code's own internal spawns, not just user tool calls:
git.exe --no-optional-locks status --shortandgit log --oneline -n 5(parent = claude.exe)gh.exe pr view <branch> --json ...(parent = claude.exe)cmd.exe /c tasklist | findstr /I "Code.exe Cursor.exe ..."(IDE detection, parent = claude.exe)bash.exewindow per configured hook (SessionStart / PostToolUse / Stop)Workarounds we applied (consolidating hooks into one orchestrator per event, wrapping MCP servers in a pythonw launcher with STARTUPINFO+SW_HIDE and a kill-on-close Job Object, switching the default terminal from Windows Terminal to Console Host) reduce it a lot, but the internal git/gh/tasklist spawns cannot be hidden from user side at all.
Feature request: spawn internal child processes and hook/MCP commands with CREATE_NO_WINDOW (or a settings.json option like "hideChildConsoles": true) on Windows.
curious if you tried my solution above? https://github.com/anthropics/claude-code/issues/14828#issuecomment-4253966091
Adding a data point + a diagnostic method, in case it helps others triage.
On a Windows box with a lot of automation, I had the same unbearable flashing — dozens of PowerShell/conhost/OCR windows per second stealing focus. When I actually traced what was spawning each window, most of my storm was not Claude Code's tool calls. The two dominant sources were:
LogonType = Interactive("Run only when user is logged on"). Those run in the visible desktop session (session 1), so every console child they spawn — PowerShell, cmd, and especiallytesseract.exe/pdftoppm.exein OCR pipelines (one window per page) — flashes. The task's "Hidden" checkbox and-WindowStyle Hiddenare cosmetic and do not stop this.Diagnostic: attribute each flash to its real source
Run in an elevated PowerShell — this shows console processes in your visible session and their parent, so you can see whether Claude Code, a Scheduled Task (
svchost … Schedule), or some app is the spawner:The fix for the non-Claude sources
The key realization: what decides visibility is the session, not the "Hidden" flag. Interactive logon = session 1 = visible; SYSTEM/ServiceAccount (or "Run whether user is logged on or not") = session 0 = invisible.
Export-ScheduledTaskevery interactive task to XML so it's reversible.``
powershell
``$p = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
Set-ScheduledTask -TaskName 'YOUR_TASK' -Principal $p
S4Ucannot be set for a different user by SYSTEM (Access denied 0x80070005).creationflags=subprocess.CREATE_NO_WINDOWon Windows (common with pytesseract/pdftoppm/ffmpeg).Eliminating those two sources removed the overwhelming majority of my flashing. The remaining Claude-Code tool-spawn flashes are what this issue is about — this isn't a fix for that, just a way to rule out (and kill) the other contributors so you can see how much is actually the tool spawns.
I fully agree. I hope this gets fixed soon because it's really disruptive when trying to work.
Building on the tracing approaches above (@romemozu's process-creation events, @feelgoodfarms' method): before assuming every flash is Claude Code's missing
windowsHide, it is worth separating two sources - Claude Code's own spawns vs plugin hook configs launched through a visible shell (claude-mem-class"shell": "bash"entries, login-shell-lflags). The second kind is fixable today.I packaged the whole triage-and-fix loop I ended up building: a window sampler (logs class + owning process + command line of every new visible window while you reproduce), a scanner for the known flash-causing hook shapes across Claude Code / Codex / Cursor plugin dirs, and a fixer that wraps offenders in a locally-compiled GUI-subsystem launcher (the only spawn that cannot flash -
powershell -WindowStyle Hiddenprovably still flashes on Win11 + Windows Terminal before it hides): https://github.com/NiceLeader/agent-hooks-doctorOn my machine that eliminated the majority of the flashing; what remains is genuinely this issue and needs the one-line
windowsHide: trueupstream.Adding a data point that might help others hit this: my
CLAUDE_CODE_GIT_BASH_PATHwas pointing atgit-bash.exe(the GUI/mintty launcher) instead of the actual console binary.git-bash.exeis a windowed app by design, not a console-subsystem process, so nowindowsHide/CREATE_NO_WINDOWflag Claude Code passes internally can suppress it. It's always going to draw a window no matter what.Pointing the env var at the real binary instead fixed it completely for me:
(path will vary depending on where Git for Windows is installed; look for
usr\bin\bash.exe, notgit-bash.exe/git-cmd.exe)No flashing on any Bash tool calls after restarting Claude Code so the new env var takes effect.
This is basically the same idea @DamianReeves posted above (pointing
SHELLat a realbash.exerather than a wrapper), and matches theCLAUDE_CODE_GIT_BASH_PATHworkaround @ilude listed in the consolidated writeup. Worth double-checking if you have either var set at all, make sure it resolves tobash.exe, not a GUI launcher.Environment: Windows 11, Claude Code CLI v2.1.81 (npm install), Git installed at a custom path (not the default
Program Files\Git).@ayer-ribeiro great catch - that is a genuinely distinct shape:
git-bash.exeis the mintty GUI launcher, a windowed app by design, so nowindowsHide/CREATE_NO_WINDOWthe runtime passes can ever suppress it. Nothing upstream can fix that one; only the config can.Added detection for exactly this misconfig to the doctor I linked above - it now flags
CLAUDE_CODE_GIT_BASH_PATHpointing atgit-bash.exe(env var or settings.json) and suggests the console binary path (...\bin\bash.exe), credited to you in the README.Still present in 2.1.220, and it reached a path that was previously quiet for me: the
statusLinecommand.What changed: my
statusLinein~/.claude/settings.jsonhas runpowershell -ExecutionPolicy Bypass -File …statusline.ps1unchanged since 2026-07-23, with no visible window. After the auto-update 2.1.219 → 2.1.220 (2026-07-30 05:36 UTC) the same command flashes a console window on every statusline refresh — i.e. after every message and tool call, so it's near-constant.Nothing local changed:
settings.jsonmtime 2026-07-23, hook scripts mtime 2026-06-26. The CLI update was the only change on the machine, and the flashing started the same day.Environment: Windows 11 Home 26200 · Claude Code 2.1.220 (npm-global) · Node v24.15.0 ·
statusLinetypecommand.If it helps narrow the spawn site: the
SessionStart/UserPromptSubmithooks in the same settings file runnoderather thanpowershell. I'm switching the statusline tonodeas a workaround and can report back whether that path flashes too.Correction / broader than my last comment: on this machine the flashing is not limited to the
statusLinepath. EveryBash/PowerShelltool call in 2.1.220 flashes a console window too — confirmed by watching it fire in lockstep with aghinvocation.This user saw zero window flashing on 2.1.219 and earlier — both the tool-spawn and statusline paths were silent — so for at least some Windows setups 2.1.220 looks like a regression in subprocess window-hiding generally, rather than the long-standing behaviour described in the original report.
Retracting my two earlier comments in this thread — the attribution was wrong and I don't want it left as bad data in a bug report.
I claimed console-window flashing appeared with the 2.1.219 → 2.1.220 update. That does not hold:
powershell.exe … -WindowStyle Hidden). Watching the process tree tied each flash to that task's exact run time, to the second.So this environment is not evidence of a 2.1.220 regression, and the version boundary I described should be disregarded. Sorry for the noise.
One incidental note that may still be useful to others debugging this class of problem:
-WindowStyle Hiddenonpowershell.exedoes not prevent the flash, because Windows allocates the console host before PowerShell can apply the window style. A scheduled task has to run non-interactively (S4U) to be genuinely windowless.Still present in 2.1.207 (VSCode extension, Windows 11 Home 10.0.26200, Git Bash / MSYSTEM=MINGW64).
Adding two things I did not see covered in the thread: an explicit elimination of my own software as the source, and a list of the configuration paths that do not work around it.
Ruled out my own software first
My property surveillance service was spawning
ffmpegsubprocesses withoutCREATE_NO_WINDOW. Fixing that removed 4 persistent windows — and the flashing from tool calls remained, in lockstep with eachBash/PowerShellinvocation. So on this machine the remaining windows are demonstrably Claude Code's own spawns, not a third-party process.Worth stating because "check your own subprocesses" is a reasonable first response, and here it was genuinely part of the picture — just not all of it.
Configuration paths that do not work around it
| Attempted | Why it does not help |
|---|---|
|
envinsettings.json| sets environment variables, not process creation flags || switching shell (Git Bash → pwsh/cmd) | the issue is in how the process is spawned, not what is spawned |
| Windows Terminal default profile | not involved —
bash.exeis spawned directly, never via Windows Terminal ||
--allowed-tools/--disallowed-tools| controls which tools may run, not spawn flags || hooks | cannot influence the caller's spawn flags |
Why this scales badly with agentic use
The impact is not the window itself, it is the keyboard focus theft. Focus is pulled away mid-word while typing, once per tool call. A single long autonomous session runs to several hundred tool calls.
That inverts the value of agentic mode: the more independent the agent is, the less usable the machine becomes while it works. Concretely, I am building a camera surveillance system (6 cameras, YOLO detection, ~175 automated tests) interactively with Claude Code, and long unattended runs are exactly the workflow this punishes hardest.
Request
windowsHide: trueon thechild_process.spawn()calls — both the shell-snapshot path and hook execution, per the analysis in #64163.Happy to test a pre-release build on this setup if that is useful.
Adding a diagnosed reproduction from daily heavy use, in case it helps prioritize.
Environment: Windows 11 Pro (10.0.26200), Claude Code v2.1.212 native (not WSL), Git Bash installed.
Setup that triggers it:
statusLineis"type": "command"invokingbash ~/.claude/scripts/statusline-command.sh, plus several hooks (PreToolUse/PostToolUse/Stop/PreCompact) each invokingbash ~/.claude/scripts/*.sh. Around 13 concurrent Claude Code sessions open across VS Code windows.Observed: a black console window flashes on screen every few minutes, even when all sessions are idle. It is fast enough that no title bar is readable, which made it hard to attribute (we first chased an unrelated third-party scheduled task).
Evidence: a 2-minute process-launch poller (Win32_Process snapshot diff at 150 ms) caught the spawn chains repeatedly:
Three such chains in one 2-minute window from a single session; with 13 sessions the statusline refreshes alone produce a visible conhost flash every few minutes. We also observed 25 lingering hidden conhost.exe processes owned by these chains.
Impact: constant focus-stealing flashes during normal work; also makes users suspect malware (an anonymous black window firing every few minutes is indistinguishable from one at a glance).
Ask: pass
windowsHide: true(and ideallyCREATE_NO_WINDOW) on thechild_process.spawncalls used for statusLine commands and hook commands on win32. Happy to test a build.Correction / retraction of my comment above.
On closer measurement, I was wrong to attribute the flashing to the statusLine and hook path on this version. Re-testing on v2.1.212 native (Windows 11):
windowsHide: trueon this build - all three spawn forms, including the statusLine path (spawn(cmd, [], {..., windowsHide: true})).EnumWindowswatcher polling every 30-40 ms over 1191 statusLinebash.exespawns in ~50 s (13 concurrent sessions) recorded zero new visible top-level windows of any class (ConsoleWindowClass/PseudoConsoleWindow).conhost.exeprocesses are created but no window ever becomes visible.So on 2.1.212 the statusLine/hooks are not the source of the visible flashing, contrary to my earlier comment.
The console spawns I can still see being created are stdio MCP servers, launched as:
(grandparent
claude.exe, allocating a fresh console0x4). If anyone is still getting flashes on a current build, worth checking whether thewindowsHideflag is being passed on the MCP stdio server spawn path specifically, as opposed to the statusLine/tool paths - they appear to be separate spawn sites. Repro is trivial: configure any stdio MCP server whosecommandisnpx(so it resolves throughcmd.exe) and watch for a console flash each time the server boots/reconnects.Happy to test a build with
windowsHideon the MCP spawn path.All the diagnostics I've done point to the statusline. I installed ccstatusline, got flashing windows; replaced ccstatusline with a custom powershell script, still get flashing windows. Disabled all my stdio MCPs (had Aspire and cwm-roslyn-navigator), still got flashing windows. Disabled my custom statusline altogether, and no more flashing windows.
Adding a data point that isolates this to Claude Code CLI's spawn of hook commands, not the plugin/tool being invoked.
What we ruled out downstream
I opened alexgreensh/token-optimizer#104 about the same flashing during normal work on Windows and tried three progressively more aggressive workarounds inside the plugin:
wscript //Bwrapper around every hook command — no visible reductionpythonw.exepreference in the plugin's own launcher (later landed upstream by the plugin author as_maybe_swap_to_pythonw) — no visible reduction eitherbash.exeentirely by rewritinghooks.jsonto invokepythonw run.pydirectly as the first token — no reduction (user reported it looked slightly worse)After the plugin author landed v5.11.72+ with the console-safe python swap and fixed a separate console-allocating Scheduled Task in the plugin itself, the observation on my Windows box was:
Since no downstream fix removed the per-tool-call flash, the console allocation must be happening in the
cmd.exe /c "<hook command>"wrapper that Claude Code CLI uses to spawn the hook — before any bash, python, or wscript inside it can matter. This is consistent with @mattgphoto's process sampling in #104 (111bash.exevs only 2cmd.exein 28s of agentic use, but the transientcmd.exe /cstill allocates a console).Concrete ask
If Node's
child_process.spawn(cmd, { shell: true, ... })for hook execution passedwindowsHide: true, thecmd.exe /cshim would still exist but its transient console wouldn't be shown. That's a one-line change on the CLI side and would eliminate the flash regardless of what the hook command does inside (bash, python, wscript, direct .exe).The plugin author documented this as an external limit in-source in v5.11.72+ (comment in #104).
Environment
shell: trueHappy to test a candidate build if one lands.
Reproduced this independently and want to share two findings that add some precision beyond "a console briefly appears" — one about why it can look like a full terminal window rather than a faint flash, and one about what's actually triggering it in a hook-heavy setup.
It's not always just a
conhost.exeflash — it can be a fullWindowsTerminal.exewindowIf
HKCU:\Console\%%StartuphasDelegationConsole/DelegationTerminalset to Windows Terminal's GUID ({2EACA947-7F5F-4CFA-BA87-8F7FBEEFBE69}/{E12CFF52-A866-4C77-9A90-F570A7AA2C6B}) — the default on many Windows 11 setups — then any unhidden console-allocating child process gets delegated to a brand-newWindowsTerminal.exe -Embeddingprocess, not just a bare conhost. That's a much more visually disruptive and confusing symptom than a "flash": it reads as an entire terminal application popping up and disappearing, with its own taskbar entry, which is probably why some reports in this thread describe "a program opening" rather than a flicker.I confirmed this is really happening (not just theorized) by polling window visibility directly via the Win32 API rather than only watching for process creation:
This caught multiple genuinely-new
WindowsTerminal.exe -Embeddingprocesses (fresh PIDs each time,IsWindowVisible = true, non-nullMainWindowTitle) appearing and disappearing within the same second-or-two window — i.e. directly-observed, not inferred from CPU/process churn.What's triggering it: PostToolUse
commandhooks, compounded across concurrent sessionsCross-referencing a WMI
__InstanceCreationEventprocess trace against the exact timestamps the visible windows appeared, the dominant activity at each flash was aPostToolUsehook (acommand-type hook registered insettings.json, e.g. one that runs after everyBashtool call) spawningbash.exe— sometimes as a nestedbash.exe -c "bash.exe ...script.sh"chain. Each spawn gets its ownconhost.exe.Two things made this much worse in my repro, which might explain why severity varies so much between reports:
Bash-matchedPostToolUsehook fires once per Bash call; a config with several matchers (Bash|Edit|Write|MultiEdit|Agent|Tasketc.) onPreToolUseandPostToolUsecan spawn 4–7+ child processes per single tool call.I could not pin the exact single spawn call missing
CREATE_NO_WINDOW/windowsHide— dozens of hook spawns fired per minute while only a handful produced a visible window, so whatever's happening is inconsistent rather than universally broken (i.e. most hook spawns are properly hidden; something about a subset isn't). But the mechanism — hook-command spawn → occasionally-visible window, worse with more hooks and more concurrent sessions, and rendered as a fullWindowsTerminal.exewindow rather than a faint flash when Terminal delegation is enabled — seems like a useful, reproducible angle for narrowing down the actual missing flag.Happy to share the full WMI trace / polling script if useful for a maintainer trying to reproduce this deterministically.
Following up on my own comment above, and on @lnorton89's registry finding, with
a measurement of what that delegation setting actually costs.
Short version:
DelegationConsole/DelegationTerminaldoes not just change howthe flash looks. It decides whether a lingering console host costs ~2 MB or
grows unbounded. I have ~6.5 days of process-memory sampling across both
configurations on the same machine.
First, a correction to my own table above: I listed "Windows Terminal default
profile — not involved". That entry was about which profile Windows Terminal
opens, and it stands. The console delegation setting is a different mechanism
and it turns out to matter a great deal.
1. Claude Code does leave lingering console hosts (reproducing @cyneta's "25 hidden conhost")
Measured just now:
They accumulate and survive for days. On Console Host delegation this is
harmless — the 93-hour-old one holds the same 1.9 MB as the 46-hour-old ones,
so the host itself does not grow. That is the control for what follows.
2. On Windows Terminal delegation, the host grows ~320 MB/h for as long as the console lives
Same machine, earlier, when a console-allocating child was delegated to
WindowsTerminal.exe. Sampled every ~5 min by a memory watchdog writing CSV;recomputed from that raw CSV, two independent
WindowsTerminal.exeinstances:Two separate processes, near-identical rate. Growth is time-driven, not
output-driven: split into four 6-hour bands the rate is 316 / 365 / 320 / 321
MB/h regardless of whether anything was being printed. A buffer filling with
console output would be near zero on an idle night.
Effect on system memory pressure, same CSV:
Honest scoping — the trigger was mine, not Claude Code's. The process that
allocated that console was my own long-running service, launched with
CREATE_NO_WINDOW | DETACHED_PROCESS.DETACHED_PROCESSwins, the child gets noconsole, and when it later starts a console program Windows must create a fresh
one. Dropping
DETACHED_PROCESSmade theWindowsTerminal.exeprocess disappear,which is what pins the causality. This is the same "rule out your own software"
exercise as in my earlier comment, one layer deeper.
So I am not claiming Claude Code leaked 13 GB — its hook consoles are
transient. What I am claiming is that the two halves meet: Claude Code supplies
console hosts that linger for days (§1), and the delegation setting decides
whether such a host is a 2 MB conhost or a Windows Terminal process on a
~320 MB/h ramp (§2). On a default Windows 11 install pointing at Windows
Terminal, that combination is reachable without anyone doing anything unusual.
3. A mitigation that costs nothing while the flag question is settled
Setting the default terminal application to Windows Console Host caps the damage
when a spawn does get a visible console: a conhost flash instead of a full
WindowsTerminal.exe -Embeddingwindow with its own taskbar entry, and themeasured growth does not occur. Current state here, with Windows Terminal
1.24.11911.0 still installed:
(Settings → System → For developers → Terminal, or Windows Terminal's own
"Default terminal application" dropdown.)
It is a mitigation, not a fix. It makes the missing
windowsHidecheap instead ofexpensive, and does nothing about the flashing itself. The request in my earlier
comment stands.
Happy to share the watchdog CSV or the sampling script if that helps anyone
reproduce this deterministically.
Environment: Windows 11 Home 10.0.26200, Claude Code 2.1.207 (VS Code extension),
3 hooks configured (one
PostToolUseonEdit|Write, twoUserPromptSubmit), nocustom statusLine. Sampling window 2026-08-03 → 2026-08-10, 1919 samples.
@sorenssl I can't really tell if you're positing that WindowsTerminal.exe memory usage grows unbounded as a general rule, but it's worth pointing out that on my system, it doesn't. I have one process running on my machine that has been running around 9 days and has ~174MB WS and ~616MB PM. It's currently running 5 tabs, 3 of which have claude.exe running in them.
Confirming the Terminal-delegation mechanism from a different angle, plus a
narrower hypothesis for why only some hook spawns leak a window.
Short recap of what I posted in July, since it is the premise here: a
console-subsystem process gets its window created VISIBLE before it can hide
itself, so
powershell -WindowStyle Hiddenprovably still flashes on Win11 withTerminal delegation, while a GUI-subsystem (
winexe) binary spawned identicallydoes not.
On why it is inconsistent: nesting, not hook count
@lnorton89 notes that most hook spawns are hidden and only a subset leaks. My
data points the same way, and suggests the split is nesting.
On this machine I have two groups running side by side. Five hook entries whose
command is a single direct
node "<script>" <args>have never produced a window,across months. A plugin whose seven hook commands ran under
bashwithexport PATH="$($SHELL -lc 'echo $PATH' 2>/dev/null):$PATH"at the head producedthem constantly, including on
PostToolUsewithmatcher: "*", so once per toolcall. On Windows
$SHELLis/bin/bash.exe, so that prelude is a full loginshell spawned to read one variable.
That matches the
bash.exe -c "bash.exe ...script.sh"chains in your WMI trace.The plausible reading is that the runtime hides the process it starts, and the
leak is the grandchild the hook's own command line spawns, which nothing passes
CREATE_NO_WINDOWto.If that holds, the fix is narrower than "hide all hook spawns": a hook command
that spawns its own shell is outside the runtime's control, so either the flag
has to survive into the grandchild, or the hook has to be started in a way that
cannot allocate a console at all.
Two things follow for anyone hitting this now:
broken. The plugin above has a fix in flight (thedotmack/claude-mem#3519)
that drops the login shell when
nodeis already on PATH.symptom regardless of which spawn is at fault, since there is no console to
create. That has held for me across four consecutive releases of that plugin,
with the hooks still executing normally.
What I cannot answer
Same as @lnorton89: I never pinned the specific spawn site missing
CREATE_NO_WINDOW/windowsHide. The launcher approach sidesteps the questionrather than answering it, so this is a mitigation, not a diagnosis. A runtime fix
is still worth having.
Unrelated latency note, since hook count came up: running several handlers in one
process instead of one process per handler is worth doing on Windows for its own
sake. Node startup is around 75 ms here, so three separate handler spawns on
every
Writewere adding roughly 225 ms of pre-tool latency. That is aperformance fix, not a window fix.
@godefroi Good push — and no, unbounded growth as a general rule is not what I
meant, and your 9-day instance shows it would be wrong. I ran a controlled test
overnight to find where the line actually sits. It turns out your case and mine
differ by one thing, and it is not the application.
Two phases, 75 minutes each, same machine, same night, 76 samples per phase at
60 s intervals.
Phase A behaves the way you describe: no measurable growth over 75 minutes
(the -1 MB/h is noise, not a decline), with handle count trending down,
929 -> 892.
Phase B pointed
DelegationConsole/DelegationTerminalat Windows Terminal andrecreated the chain behind my original numbers: a process started with
CREATE_NO_WINDOW | DETACHED_PROCESS(soDETACHEDwins and it gets no console),which then starts a console program — forcing Windows to create a fresh console,
which delegation hands to Windows Terminal.
The two obvious objections are both ruled out by the data:
292 MB/h. Straight line for 75 minutes, no flattening.
time.sleep(). It writes nothing atall. This matches the original incident, where the rate was essentially
constant across all four 6-hour bands of the day — 316 / 365 / 320 / 321 MB/h
— which is not the shape an output-driven buffer produces.
So the precise claim, narrower than the one I implied: Windows Terminal does
not leak as an application — it leaks in the role of console host for a process
that has none of its own. Same binary, two roles, and the entire difference
between them is the leak. You are looking at the first role. I was looking at
the second.
That distinction is what makes it relevant here rather than just my own bug.
Claude Code leaves console hosts parented to
claude.exealive for days — 8 onthis machine right now, aged 44 h to 116 h. Under Console Host they cost
1.9–2.5 MB each and do not grow with age: the 116-hour-old one holds 1.9 MB,
exactly what the 70-hour-old ones hold. Under Terminal delegation, that same
lingering host is on the ramp above. The registry value decides which of the
two you get.
One piece of credit I owe from my previous comment: @romemozu listed "switching
the default terminal from Windows Terminal to Console Host" among their
workarounds back on 6 July, a month before I presented the same switch as if it
were new. What I can add is why it is worth more than a cosmetic fix — the
numbers above are the difference between the two settings. Their caveat still
stands too: the internal
git/gh/tasklistspawns fromclaude.execannot be hidden from the user side at all. That fits what I see here — three
hooks configured, eight lingering hosts.
Scope, since one machine is one machine:
-Embeddingis thereasonable reading of what it was, not something I proved.
created by Windows. That is the point of the comparison, but it is more than a
one-variable difference.
And credit where it is due: your counter-example is what produced this. I had a
rate and no boundary, which is a claim rather than a finding — it took an
instance that demonstrably does not leak to turn "does Windows Terminal leak?"
into a question with an answer. One machine disagreeing with numbers was worth
more than agreement would have been.
The test is two files — a PowerShell harness that flips the setting, samples,
and restores it, plus the three-level process chain. I will post both if anyone
wants to run the same two phases elsewhere; a second machine would settle the
scope question faster than more measurements from mine.
Correction to my previous comment.
I ended it with a claim my own test did not isolate: that Windows Terminal leaks
"in the role of console host for a process that has none of its own". The two
phases changed two variables at once — who created the Terminal process, and
whether the hosted process had a console of its own — and I attributed the whole
effect to the second.
Here is the missing control. Phase C: Terminal as the delegated console host
again, but hosting an entirely ordinary process, launched with
CREATE_NEW_CONSOLEfrom a parent that has its own console. NoDETACHED_PROCESS, no console-less parent anywhere in the chain.C matches B. The
DETACHED_PROCESSchain has nothing to do with it — it wasjust the path I happened to find this through. The qualifier in my sentence is
wrong, and the corrected claim is both simpler and broader:
Windows Terminal grows ~320 MB/h whenever Windows makes it the delegated
console host, regardless of how the hosted process was started. It does not grow
as a terminal you opened yourself.
That makes the delegation setting the entire variable, which if anything
sharpens the practical point in this thread: any console-allocating spawn that
is not hidden lands on that path when delegation points at Terminal — including
the hook and MCP spawns being discussed here.
Two notes on method, since I got one of them wrong:
Start-Processdid not create anew console, so no host was ever delegated. Because I logged conhost,
OpenConsole and WindowsTerminal separately with their parents, this showed up
as "no new console at all" rather than a result. Without that instrumentation
I would have reported "Windows does not delegate ordinary processes", which is
false.
than a leak: the chain ran once, one Terminal PID throughout, and the
per-minute deltas show no steps — 74 intervals, mean 5.26 MB, none above 3x
the mean. A scheduled console process on this machine did fire mid-phase; that
minute grew 6.1 MB against neighbours of 5.5, 5.5, 7.5 and 4.9. Thread count
fell from 92 to 86 across the phase. Adoption would show steps and rising
threads; neither is present.
Same machine and Windows Terminal version as before (Windows 11 Home 26200,
WT 1.24.11911.0), so the scope caveat stands: one box.
@sorenssl
Real talk here, drop your LLM for a second and turn on your brain. microsoft/terminal does have some reported "memory leak" issues, but every real reported issue of that type is tied to output volume, and the one you believe you've discovered here is over 2x the known worst-case output-driven scenario. If a bug like that existed, it'd be one of the most trivially reproducible, universally-triggered bugs in a massively-used, actively-staffed Microsoft repo (any detached process that allocates a console under default Windows 11 settings). It isn't filed anywhere.
Two independent data sets in this thread now point at the same spawn-path property, so here is a short synthesis for whoever picks this up.
bash -c "..."even when argv[0] is a plain.exe.bash.exeis a console-subsystem binary, so each dispatch materializes a conhost/Terminal window regardless of the child's own subsystem or any-WindowStyle Hiddenon the inner command.node "<script>" <args>never produced a window across months, while seven bash-wrapped hook commands flashed constantly. Same split, measured from the other side.The actionable slice for maintainers is still small: when a hook command's argv resolves to a single executable with no shell metacharacters, spawn it directly with
windowsHide: trueinstead of going through the bash wrapper; keepwindowsHideon the wrapper spawn for genuine shell commands. That removes the dominant flash source for native-binary hooks without touching the general Bash-tool path.I ran a controlled A/B on the wrapper question, since two readings of it were
circulating in this thread.
Setup: the same executable declared twice as a
PostToolUsehook in onesettings file, differing only in declaration form. Isolated headless session
(
claude -p --settings <file>), process creation watched viaWin32_Processwith parent resolution. Payloads write a marker file and then sleep, so nothing
is lost to sampling.
Both fired. Result:
| form | processes created | parent of the exe |
|---|---|---|
| A, string |
bash.exeandlauncher.exe| wrapper present || B,
args|launcher.exeonly |claude.exe, directly |@marcodelpin's premise holds. A hook command written as a string is wrapped
in
bash -ceven when argv[0] is a plain.exewith no shell metacharacters.I had read a difference between two sets of hooks on my own machine as evidence
that the runtime inspects argv[0]. It does not. The difference was the
argsfield, and I was wrong about the cause.
What is new:
argsis an existing bypass. Declaringcommandplus anargsarray makes the runtime spawn the executable directly, with no shell inthe chain. That works today, with no runtime change, and it is why hooks
rewritten into that form have no bash parent. Any plugin whose hook command is
a fixed executable can convert to it now.
It also means the direct-spawn machinery already exists. The distance between
that and @marcodelpin's proposal is only the derivation step: deriving
argsautomatically when a string command has no shell metacharacters would route
those commands through a path that already works.
Scope note so this is not read as more than it is. This measures spawn shape,
not which spawn allocates the visible window. On this machine the bash wrapper
itself has never produced one, so my flashes came from further down the chain.
That may differ from the environment in @marcodelpin's measurements, and the
two questions are worth keeping separate.
A scoping correction to my Aug 10/12 comments, after trying to reproduce the
flash deliberately on this machine and failing in every configuration.
I ran the grandchild matrix from a hidden parent console:
bash -> node(2.5 s sleep),
bash -> node -> spawnSync(bun, {windowsHide:true}),bash -> node -> spawnSync(bun)with no flag at all, andspawn(bun, {detached:true, stdio:'ignore'})mirroring a daemon respawn.Window visibility polled via
IsWindowVisible, not process creation. Zerovisible windows in every cell, including the deliberately-unhidden ones.
Then I checked the one machine property @lnorton89 flagged:
HKCU:\Console\%%Startuphere is{B23D10C0-E52E-411E-9D5B-C09FDF709C7D}-classic conhost delegation, not Windows Terminal. Under conhost delegation a
child of a hidden console inherits that console, so an unhidden grandchild has
nothing to pop.
Two consequences:
is delegation-scoped evidence, not a general result. On WT-delegation
machines (
{2EACA947-...}/{E12CFF52-...}) the same spawn shapes maybehave differently, and my machine cannot arbitrate which layer of the
chain allocates the visible window there.
that registry value; a conhost-delegation machine may be structurally
incapable of the dramatic per-call
WindowsTerminal.exesymptom. One-liner:Get-ItemProperty "HKCU:\Console\%%Startup" | Select Delegation*For the record, one theory I tested and refuted rather than posted: a
detached:true, stdio:'ignore'daemon respawn from a hidden parent does NOTcreate a window here - libuv passes DETACHED_PROCESS on Windows, so the child
gets no console at all, despite the Node docs' "own console window" phrasing.
Confirming this reproduces on a vanilla install before any hooks or plugins are involved: with just Claude Code + Git Bash out of the box, every Bash tool call flashes its own console. The details below add a data point on how a hooks-heavy setup multiplies the frequency on top of that baseline.
Environment
node.exeObserved
Baseline (vanilla Claude Code, no hooks/plugins):
bash.exewith a visible console flash. Command output and exit status are unaffected — purely visual.With the hooks-heavy setup layered on top:
node.exethrough acmd.exewrapper, which allocates its own console. PreToolUse/PostToolUse match all tools, so a single tool call stacks 2–4 distinct flashes on top of the baseline (bash.exe+ hook spawns); a typical session (~40 tool calls) racks up 80+ flashes.Tried
settings.jsonoption appears to control window suppression for spawned children.Given what #27115 found (flashing persists even where
windowsHide: trueis set on the spawn call, pointing at thecmd.exewrapper layer), it looks like each spawn path needsCREATE_NO_WINDOWapplied consistently — or a single globalwindowsHide-style setting covering all child spawns (shell tools, hooks, statusline, snapshots, MCP stdio) would resolve this whole family at once.Happy to test any experimental build or flag on this machine.