Skip to content

Windows: Console window flashing when executing tools #14828

Open

Description

@guanknow

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report (please file separate reports for different bugs)
  • 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

  1. Run Claude Code on Windows
  2. Execute any command or let Claude use any tool
  3. 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

Activity

  1. GigiTiti-Kai commented on Dec 27, 2025

    @GigiTiti-Kai

    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-mem plugin registers hooks for SessionStart, UserPromptSubmit, PostToolUse, and Stop events that run node and bun commands. Each execution creates a visible console window.

    What I tried (unsuccessfully)

    1. VBS wrapper with hidden window - Wrapped commands in a VBS script using WScript.Shell.Run with vbHide flag. Failed due to quote escaping issues with cmd /c.

    2. PowerShell -WindowStyle Hidden - Wrapped commands with powershell -WindowStyle Hidden -Command "...". The PowerShell window was hidden, but the spawned node.exe/bun.exe processes 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 the CREATE_NO_WINDOW flag when calling CreateProcess().

    Environment

    • OS: Windows 11
    • Terminal: Windows Terminal
    • Claude Code with claude-mem plugin

    Request

    Please consider implementing CREATE_NO_WINDOW for all child process spawning on Windows, including:

    • Tool execution (Bash, Grep, etc.)
    • Plugin hook execution
    • MCP server processes

    This would significantly improve the Windows user experience.

  2. TheDan64 commented on Jan 3, 2026

    @TheDan64

    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

  3. EngDawood commented on Jan 11, 2026

    @EngDawood

    yes , it's very annoying

  4. PakAbhishek commented on Jan 11, 2026

    @PakAbhishek

    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 the windowsHide option:

    Current Code:

    let V = spawn(command, [], {env: K, cwd: a1(), shell: true})

    Required Fix:

    let V = spawn(command, [], {
      env: K,
      cwd: a1(),
      shell: true,
      windowsHide: process.platform === 'win32'
    })

    Precedent:

    This exact fix was already implemented in:

    Why Workarounds Don't Work:

    All attempted workarounds fail because:

    • PowerShell -WindowStyle Hidden → cmd.exe spawns first before PowerShell
    • VBScript wrappers → Can't handle stdin properly (hooks need JSON input via stdin)
    • Batch files with start /b → Still creates initial cmd.exe window
    • PATH shims → Would also flash windows

    The ONLY solution is fixing the spawn call at the source.

    Impact Scope:

    This affects:

    • ✅ All Windows users of Claude Code
    • ✅ Every tool execution (Bash, Read, Write, etc.)
    • ✅ Every plugin hook execution (SessionStart, UserPromptSubmit, PostToolUse, Stop)
    • ✅ MCP server startups via hooks

    Business Impact:

    For professional Windows users, this makes Claude Code nearly unusable. The constant window flashing:

    • Disrupts typing mid-sentence
    • Breaks focus during code reviews
    • Makes screen recording unprofessional
    • Prevents use in client-facing environments

    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:

    • OS: Windows 11 64-bit
    • Claude Code version: v2.1.2
    • Plugin: claude-mem v9.0.0
    • Terminal: PowerShell 7.x

    Thank you for considering this!

  5. Assist-Frontier commented on Jan 18, 2026

    @Assist-Frontier

    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:

    • Claude Code for VS Code extension: 2.1.11
    • OS: Windows 11
    • Where it happens: VS Code (integrated terminal / Claude panel) — every Bash/tool invocation triggers a brief node.exe/console window flash

    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.

  6. jphoenix-dev commented on Jan 20, 2026

    @jphoenix-dev

    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.

  7. DamianReeves commented on Jan 25, 2026

    @DamianReeves

    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):

    {
      "autoUpdatesChannel": "stable",
      "env": {
        "SHELL": "C:\\Users\\redacted\\scoop\\apps\\git\\2.52.0\\bin\\bash.exe"
      }
    }
  8. Jetski5822 commented on Jan 27, 2026

    @Jetski5822

    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.

  9. blueberrynotstraw commented on Jan 29, 2026

    @blueberrynotstraw

    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

  10. Clinteastman commented on Feb 4, 2026

    @Clinteastman

    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?

  11. jarrieta86 commented on Feb 10, 2026

    @jarrieta86

    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\

  12. 56 remaining items

  13. cyneta commented on Jul 30, 2026

    @cyneta

    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):

    • The bundled runtime's statusLine/hook spawner does pass windowsHide: true on this build - all three spawn forms, including the statusLine path (spawn(cmd, [], {..., windowsHide: true})).
    • Empirically: an EnumWindows watcher polling every 30-40 ms over 1191 statusLine bash.exe spawns in ~50 s (13 concurrent sessions) recorded zero new visible top-level windows of any class (ConsoleWindowClass / PseudoConsoleWindow). conhost.exe processes 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:

    C:\Windows\system32\cmd.exe /d /s /c "npx @playwright/mcp@latest"
    

    (grandparent claude.exe, allocating a fresh console 0x4). If anyone is still getting flashes on a current build, worth checking whether the windowsHide flag 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 whose command is npx (so it resolves through cmd.exe) and watch for a console flash each time the server boots/reconnects.

    Happy to test a build with windowsHide on the MCP spawn path.

  14. godefroi commented on Aug 1, 2026

    @godefroi

    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.

  15. JuanjoB-Viewnext commented on Aug 3, 2026

    @JuanjoB-Viewnext

    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:

    1. VBS wscript //B wrapper around every hook command — no visible reduction
    2. pythonw.exe preference in the plugin's own launcher (later landed upstream by the plugin author as _maybe_swap_to_pythonw) — no visible reduction either
    3. Skipping the intermediate bash.exe entirely by rewriting hooks.json to invoke pythonw run.py directly 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:

    • ✅ Idle "5-minute pops" from the plugin's scheduled task: gone
    • ❌ Per-tool-call flashes during active work: unchanged

    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 (111 bash.exe vs only 2 cmd.exe in 28s of agentic use, but the transient cmd.exe /c still allocates a console).

    Concrete ask

    If Node's child_process.spawn(cmd, { shell: true, ... }) for hook execution passed windowsHide: true, the cmd.exe /c shim 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

    • Windows 11 Pro 26200
    • Claude Code (VSCode extension)
    • Node child spawn on Windows via shell: true
    • Python 3.13.14 (python.org), git-bash available
    • Reproducible with any plugin that registers hooks; more visible with plugins that fire many (Token Optimizer registers ~26 hooks across lifecycle events, so this is the loudest surface but not the only one)

    Happy to test a candidate build if one lands.

  16. lnorton89 commented on Aug 10, 2026

    @lnorton89

    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.exe flash — it can be a full WindowsTerminal.exe window

    If HKCU:\Console\%%Startup has DelegationConsole/DelegationTerminal set 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-new WindowsTerminal.exe -Embedding process, 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:

    Add-Type -TypeDefinition @"
    using System;
    using System.Runtime.InteropServices;
    public class Win32 {
        [DllImport("user32.dll")]
        public static extern bool IsWindowVisible(IntPtr hWnd);
    }
    "@
    # poll Get-Process | Where ProcessName -match 'cmd|powershell|conhost|WindowsTerminal|OpenConsole'
    # and check [Win32]::IsWindowVisible($proc.MainWindowHandle) every ~40ms

    This caught multiple genuinely-new WindowsTerminal.exe -Embedding processes (fresh PIDs each time, IsWindowVisible = true, non-null MainWindowTitle) 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 command hooks, compounded across concurrent sessions

    Cross-referencing a WMI __InstanceCreationEvent process trace against the exact timestamps the visible windows appeared, the dominant activity at each flash was a PostToolUse hook (a command-type hook registered in settings.json, e.g. one that runs after every Bash tool call) spawning bash.exe — sometimes as a nested bash.exe -c "bash.exe ...script.sh" chain. Each spawn gets its own conhost.exe.

    Two things made this much worse in my repro, which might explain why severity varies so much between reports:

    1. It scales with hook count. A single Bash-matched PostToolUse hook fires once per Bash call; a config with several matchers (Bash|Edit|Write|MultiEdit|Agent|Task etc.) on PreToolUse and PostToolUse can spawn 4–7+ child processes per single tool call.
    2. It's per-session, and sessions don't share the budget. I had two Claude Code sessions active (one Desktop-app session, one CLI-resumed session) with the same global hooks configured — both were spawning independently, so the effective flash rate roughly doubled with a second concurrent session. Anyone running multiple Claude Code windows/sessions at once (common with the Desktop app) will see this compound.

    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 full WindowsTerminal.exe window 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.

  17. sorenssl commented on Aug 10, 2026

    @sorenssl

    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/DelegationTerminal does not just change how
    the 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:

    conhost.exe children of claude.exe : 8
    ages                               : 20.8, 46.3, 46.3, 46.7, 81.9, 93.0, 93.0, 93.0 h
    private bytes, oldest (93.0 h)     : 1.9 MB
    private bytes, youngest (20.8 h)   : 2.5 MB
    all 28 conhost.exe on the machine  : 43.7 MB total
    

    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.exe instances:

    instance 1:  38.2 h   1 223 MB -> 13 679 MB   =  326 MB/h   monotonic 100 %  (n=458)
    instance 2:  12.0 h   1 541 MB ->  5 234 MB   =  308 MB/h   monotonic  98 %  (n=146)
    

    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:

    during the leak window (n=458):  median commit 77.9 %,  4 % of samples above 95 %
    after the console was gone (n=1113): median commit 80.0 %,  0 % of samples above 95 %
    

    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_PROCESS wins, the child gets no
    console, and when it later starts a console program Windows must create a fresh
    one. Dropping DETACHED_PROCESS made the WindowsTerminal.exe process 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 -Embedding window with its own taskbar entry, and the
    measured growth does not occur. Current state here, with Windows Terminal
    1.24.11911.0 still installed:

    HKCU:\Console\%%Startup  DelegationConsole  = {B23D10C0-E52E-411E-9D5B-C09FDF709C7D}
                             DelegationTerminal = {B23D10C0-E52E-411E-9D5B-C09FDF709C7D}
    WindowsTerminal.exe processes: 0        conhost.exe: 28 (43.7 MB total)
    

    (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 windowsHide cheap instead of
    expensive, 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 PostToolUse on Edit|Write, two UserPromptSubmit), no
    custom statusLine. Sampling window 2026-08-03 → 2026-08-10, 1919 samples.

  18. godefroi commented on Aug 10, 2026

    @godefroi

    @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.

  19. NiceLeader commented on Aug 10, 2026

    @NiceLeader

    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 Hidden provably still flashes on Win11 with
    Terminal delegation, while a GUI-subsystem (winexe) binary spawned identically
    does 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 bash with
    export PATH="$($SHELL -lc 'echo $PATH' 2>/dev/null):$PATH" at the head produced
    them constantly, including on PostToolUse with matcher: "*", so once per tool
    call. On Windows $SHELL is /bin/bash.exe, so that prelude is a full login
    shell 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_WINDOW to.

    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:

    1. Grep your own hooks for nested shells before concluding the runtime is
      broken. The plugin above has a fix in flight (fix(windows): guard the login-shell PATH prelude behind a node check thedotmack/claude-mem#3519)
      that drops the login shell when node is already on PATH.
    2. Wrapping hook commands so they start from a GUI-subsystem process removes the
      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 question
    rather 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 Write were adding roughly 225 ms of pre-tool latency. That is a
    performance fix, not a window fix.

  20. sorenssl commented on Aug 11, 2026

    @sorenssl

    Correction (later comment): the final claim below — that the leak is tied to hosting a process with no console of its own — is wrong. A missing control (phase C) showed ordinary processes behave identically. Corrected measurement here: #14828 (comment)

    @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  interactive wt.exe, one idle console       76.9 -> 75.5 MB   =  -1 MB/h
    PHASE B  WT as the DELEGATED console host for a     89.2 -> 485.8 MB  = 317 MB/h
             detached chain with no console of its own   96 % monotonic
    

    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/DelegationTerminal at Windows Terminal and
    recreated the chain behind my original numbers: a process started with
    CREATE_NO_WINDOW | DETACHED_PROCESS (so DETACHED wins 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:

    • Not startup allocation. First third of phase B: 297 MB/h. Last third:
      292 MB/h. Straight line for 75 minutes, no flattening.
    • Not console output. The payload is time.sleep(). It writes nothing at
      all. 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.exe alive for days — 8 on
    this 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 / tasklist spawns from claude.exe
    cannot 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:

    • Windows 11 Home 26200, Windows Terminal 1.24.11911.0, single box.
    • I did not capture the delegated process's command line, so -Embedding is the
      reasonable reading of what it was, not something I proved.
    • Phase A's process was launched by me with a profile and a tab; phase B's was
      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.

  21. sorenssl commented on Aug 11, 2026

    @sorenssl

    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_CONSOLE from a parent that has its own console. No
    DETACHED_PROCESS, no console-less parent anywhere in the chain.

    A  interactive wt.exe, started by me                          -1 MB/h   (75 min, n=76)
    B  delegated host for a DETACHED chain with no console       317 MB/h   (75 min, n=76)
    C  delegated host for an ordinary CREATE_NEW_CONSOLE process 319 MB/h   (39 min, n=40)
    

    C matches B. The DETACHED_PROCESS chain has nothing to do with it — it was
    just 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:

    • My first attempt at phase C measured nothing. Start-Process did not create a
      new 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.
    • On whether phase B's growth could be accumulation of adopted consoles rather
      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.

  22. godefroi commented on Aug 11, 2026

    @godefroi

    @sorenssl

    Correction to my previous comment.

    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.

  23. marcodelpin commented on Aug 11, 2026

    @marcodelpin

    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.

    1. Mechanism (my May 20 comment): the hook dispatch pipeline wraps every hook command in bash -c "..." even when argv[0] is a plain .exe. bash.exe is a console-subsystem binary, so each dispatch materializes a conhost/Terminal window regardless of the child's own subsystem or any -WindowStyle Hidden on the inner command.
    2. Independent confirmation (@NiceLeader, Jul + Aug 10): on one machine, five hooks invoked as a single direct 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: true instead of going through the bash wrapper; keep windowsHide on the wrapper spawn for genuine shell commands. That removes the dominant flash source for native-binary hooks without touching the general Bash-tool path.

  24. NiceLeader commented on Aug 12, 2026

    @NiceLeader

    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 PostToolUse hook in one
    settings file, differing only in declaration form. Isolated headless session
    (claude -p --settings <file>), process creation watched via Win32_Process
    with parent resolution. Payloads write a marker file and then sleep, so nothing
    is lost to sampling.

    // A - string form
    { "type": "command", "command": "C:/.../launcher.exe -CommandBase64 <payload>" }
    
    // B - command + args
    { "type": "command", "command": "C:/.../launcher.exe",
      "args": ["-CommandBase64", "<payload>"] }

    Both fired. Result:

    form processes created parent of the exe
    A, string bash.exe and launcher.exe wrapper present
    B, args launcher.exe only claude.exe, directly

    @marcodelpin's premise holds. A hook command written as a string is wrapped
    in bash -c even when argv[0] is a plain .exe with 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 args
    field, and I was wrong about the cause.

    What is new: args is an existing bypass. Declaring command plus an
    args array makes the runtime spawn the executable directly, with no shell in
    the 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 args
    automatically 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.

  25. NiceLeader commented on Aug 17, 2026

    @NiceLeader

    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, and
    spawn(bun, {detached:true, stdio:'ignore'}) mirroring a daemon respawn.
    Window visibility polled via IsWindowVisible, not process creation. Zero
    visible windows in every cell, including the deliberately-unhidden ones.

    Then I checked the one machine property @lnorton89 flagged:
    HKCU:\Console\%%Startup here 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:

    1. My earlier "the bash wrapper is hidden and never flashes here" observation
      is delegation-scoped evidence, not a general result. On WT-delegation
      machines ({2EACA947-...} / {E12CFF52-...}) the same spawn shapes may
      behave differently, and my machine cannot arbitrate which layer of the
      chain allocates the visible window there.

    2. Anyone posting "cannot reproduce" (me included, apparently) should attach
      that registry value; a conhost-delegation machine may be structurally
      incapable of the dramatic per-call WindowsTerminal.exe symptom. 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 NOT
    create 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.

  26. shangchuanqiytu-ui commented on Aug 20, 2026

    @shangchuanqiytu-ui

    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

    • Windows 11 Enterprise LTSC 2024 (10.0.26100)
    • Claude Code v2.1.226, native terminal (no WSL/Dev Container)
    • Git Bash shell backend
    • Plus oh-my-claudecode installed: PreToolUse / PostToolUse / PostToolUseFailure / SessionStart / Stop hooks and a statusline command, all spawning node.exe

    Observed

    Baseline (vanilla Claude Code, no hooks/plugins):

    • Every shell tool call spawns bash.exe with a visible console flash. Command output and exit status are unaffected — purely visual.

    With the hooks-heavy setup layered on top:

    • Each hook event additionally spawns node.exe through a cmd.exe wrapper, 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.
    • The statusline command is the most frequent offender since it renders on every UI update — near-constant flashing while typing.
    • Other spawn paths flash on their own schedules: shell snapshot at session start, IDE-detection polling, MCP stdio servers.

    Tried

    • Routing work through the PowerShell tool path doesn't help — the Git Bash snapshot/backend still flashes, and hook events fire for it too since they match all tools.
    • No settings.json option appears to control window suppression for spawned children.

    Given what #27115 found (flashing persists even where windowsHide: true is set on the spawn call, pointing at the cmd.exe wrapper layer), it looks like each spawn path needs CREATE_NO_WINDOW applied consistently — or a single global windowsHide-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.

  27. rob-alexa commented on Sep 4, 2026

    @rob-alexa

    Still reproducing on Windows 11 in the Claude desktop app (build 1.46388.2). The focus theft is the painful part: the window takes keyboard focus while I am typing, so characters land in a console that closes a second later.

    Two additions:

    • The flash follows hook spawns as well as tool calls, and the count scales with the number of hooks. Five separate PreToolUse entries in my repo flashed roughly fifty windows across a ten-call turn, which is why we consolidated them into one process. Three SessionStart hooks land the remaining burst while I type my first prompt.
    • The declaration form does not matter: structured command plus args flashes the same as a single command string.

    The workaround that fixed session start for me: point the hook at pythonw.exe, a GUI-subsystem binary Windows allocates no console for, and have it spawn the real command with CREATE_NO_WINDOW. Two catches. Pass the std descriptors explicitly, or close_fds=True leaves the child with invalid handles and its output is discarded silently. And propagate the child's exit code, or a PreToolUse guard's exit 2 stops blocking.

    Passing windowsHide: true when spawning hooks would remove the need for any of this.

    Correction: an earlier version of this comment said structured-form hooks appeared not to flash. That was wrong.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:toolsbugSomething isn't workinghas reproHas detailed reproduction stepsplatform:windowsIssue specifically occurs on Windows

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions