Describe the bug
On Windows, a long-running interactive/--plan copilot CLI session that stays open across
an in-place auto-update keeps executing from its now-renamed copilot.exe.old binary
instead of exiting or picking up the new build - and a large fraction of these orphaned
processes then spin one thread continuously at ~100% of a full CPU core, indefinitely,
until the process is killed. On a machine with several long-lived sessions left open across
multiple update cycles, this compounds: 7 of 13 copilot.exe processes were found stuck
this way on one machine, each burning a full core (visible as ~6% each in Task Manager on a
16-core box), for a sustained multi-day aggregate load of roughly one full CPU core just
from stale sessions nobody was actively using.
This is very likely an under-reported contributor to "my machine feels slow after Copilot
CLI has been running a while" reports, since:
- it requires no user action to trigger (any auto-update while a session is left open),
- it's easy to miss in Task Manager (each stuck process shows a modest per-core percentage,
not an obvious 100%, once normalized across many cores),
- and it accumulates with every update cycle a session survives.
Affected version
Observed across multiple builds: processes pinned to stale pkg extraction dirs 1.0.64,
1.0.64-3, 1.0.66-1, 1.0.69-0, 1.0.69-1, 1.0.69-2 were all still spinning while
1.0.70 was current. Not version-specific - this looks like a structural self-update issue
rather than a regression in one build.
Environment
- Windows 11, native (not WSL2)
- Install method: WinGet (
GitHub.Copilot_Microsoft.Winget.Source)
- Sessions launched via Windows Terminal tabs (both directly and via a third-party sidebar
tool that shells out to copilot.exe)
Diagnostic evidence
1. Confirmed continuous single-thread spin, not a momentary spike: re-sampled
per-thread CPU over 3 intervals (~4s each) for the 7 stuck PIDs - consistent ~3.9-4.0
CPU-seconds per 4 wall-seconds (~100% of one core) every time, with the same thread ID
reporting Running on every sample.
2. Every stuck process is executing from a renamed .old binary. Windows' own
QueryFullProcessImageName (queried per-PID) reports the live on-disk image name for these
processes as copilot.exe.old, not copilot.exe - i.e. these processes are provably still
running the file the auto-updater renamed out from under them
(rename-running-exe-to-.old, write new exe in its place is the classic in-place-update
pattern; %LOCALAPPDATA%\copilot\pkg\win32-x64\ also contains a .replaced-<version>-...
directory confirming the extraction cache's own replace-on-update logic).
- Caveat for anyone reproducing this:
Get-Process -Name copilot* (bulk/wildcard) and
Win32_Process.Name (WMI/CIM) both still report the stale "copilot" name for the
same PID even after the rename - only a targeted per-PID lookup (Get-Process -Id <pid> under PowerShell 7/.NET Core, or QueryFullProcessImageName directly) reflects
it live. Windows PowerShell 5.1 (.NET Framework)'s Get-Process -Id <pid> also shows
the stale name - only .NET Core reflects it, so a Win32 API call is the only
version-independent way to detect this.
3. Every stuck process was pinned to a superseded, numeric-suffixed pkg runtime
extraction dir (%LOCALAPPDATA%\copilot\pkg\win32-x64\1.0.64, 1.0.64-3, 1.0.66-1,
1.0.69-0, 1.0.69-1, 1.0.69-2), while healthy long-running processes checked at the
same time used un-suffixed dirs (1.0.69, 1.0.70) - a 7/7 vs 2/2 correlation.
4. An ETW CPU-sampling trace (wpr.exe -start CPU -filemode, ~8s, analyzed with
xperf -a profile -detail) targeting one stuck process showed its top CPU consumers as:
| Module |
Usage % |
ntoskrnl.exe (kernel) |
3.82 |
FLTMGR.SYS (filter manager) |
0.78 |
copilot.exe.old |
0.73 |
Ntfs.sys |
0.47 |
ntdll.dll |
0.21 |
cldflt.sys (cloud-files/OneDrive filter), luafv.sys (UAC virtualization), bindflt.sys, fileinfo.sys, Wof.sys |
~0.01 each |
i.e. the hot samples land inside the renamed image itself, dominated by kernel and
filesystem-filter-driver time - consistent with a tight retry/poll loop making constant
filesystem syscalls (e.g. against the renamed binary, the pkg cache, or a file-watch path),
not a genuine idle wait.
5. Not tab/window related: the parent shells (pwsh.exe/powershell.exe) hosting these
sessions were all still alive and responding, i.e. this isn't caused by a closed/detached
terminal tab - the CLI process itself is what's stuck.
Steps to reproduce
- Start a long-running interactive or
--plan copilot session on Windows and leave it
idle (no active turn) in a Windows Terminal tab.
- Let an in-place auto-update occur while the session is still running (this can take
days of uptime to hit naturally, or may be reproducible faster by forcing an update
cycle while a session is open).
- Check whether the still-running process's live image name has become
copilot.exe.old
(via QueryFullProcessImageName / PowerShell 7's (Get-Process -Id <pid>).ProcessName)
and whether its CPU usage has pegged one core continuously (Get-Process -Id <pid> | Select Threads shows one thread stuck in Running with a fast-growing
TotalProcessorTime).
Expected behavior
Either the CLI detects that its own binary/runtime cache has been replaced and gracefully
exits or reloads, or - at minimum - it should never spin a thread at 100% while otherwise
idle. Idle CPU should be ~0% regardless of whether an update occurred underneath a
long-running session.
Workaround
I wrote a small, self-contained PowerShell script that detects and optionally kills these
stale/spinning processes (checks the live per-PID image name via QueryFullProcessImageName
so it works correctly under both Windows PowerShell 5.1 and PowerShell 7, then optionally
re-samples CPU to confirm sustained spin before offering an interactive kill menu). Happy to
share it here or as a gist if useful for triage/repro.
Possibly related
Describe the bug
On Windows, a long-running interactive/
--plancopilotCLI session that stays open acrossan in-place auto-update keeps executing from its now-renamed
copilot.exe.oldbinaryinstead of exiting or picking up the new build - and a large fraction of these orphaned
processes then spin one thread continuously at ~100% of a full CPU core, indefinitely,
until the process is killed. On a machine with several long-lived sessions left open across
multiple update cycles, this compounds: 7 of 13
copilot.exeprocesses were found stuckthis way on one machine, each burning a full core (visible as ~6% each in Task Manager on a
16-core box), for a sustained multi-day aggregate load of roughly one full CPU core just
from stale sessions nobody was actively using.
This is very likely an under-reported contributor to "my machine feels slow after Copilot
CLI has been running a while" reports, since:
not an obvious 100%, once normalized across many cores),
Affected version
Observed across multiple builds: processes pinned to stale
pkgextraction dirs1.0.64,1.0.64-3,1.0.66-1,1.0.69-0,1.0.69-1,1.0.69-2were all still spinning while1.0.70was current. Not version-specific - this looks like a structural self-update issuerather than a regression in one build.
Environment
GitHub.Copilot_Microsoft.Winget.Source)tool that shells out to
copilot.exe)Diagnostic evidence
1. Confirmed continuous single-thread spin, not a momentary spike: re-sampled
per-thread CPU over 3 intervals (~4s each) for the 7 stuck PIDs - consistent ~3.9-4.0
CPU-seconds per 4 wall-seconds (~100% of one core) every time, with the same thread ID
reporting
Runningon every sample.2. Every stuck process is executing from a renamed
.oldbinary. Windows' ownQueryFullProcessImageName(queried per-PID) reports the live on-disk image name for theseprocesses as
copilot.exe.old, notcopilot.exe- i.e. these processes are provably stillrunning the file the auto-updater renamed out from under them
(rename-running-exe-to-
.old, write new exe in its place is the classic in-place-updatepattern;
%LOCALAPPDATA%\copilot\pkg\win32-x64\also contains a.replaced-<version>-...directory confirming the extraction cache's own replace-on-update logic).
Get-Process -Name copilot*(bulk/wildcard) andWin32_Process.Name(WMI/CIM) both still report the stale "copilot" name for thesame PID even after the rename - only a targeted per-PID lookup (
Get-Process -Id <pid>under PowerShell 7/.NET Core, orQueryFullProcessImageNamedirectly) reflectsit live. Windows PowerShell 5.1 (.NET Framework)'s
Get-Process -Id <pid>also showsthe stale name - only .NET Core reflects it, so a Win32 API call is the only
version-independent way to detect this.
3. Every stuck process was pinned to a superseded, numeric-suffixed
pkgruntimeextraction dir (
%LOCALAPPDATA%\copilot\pkg\win32-x64\1.0.64,1.0.64-3,1.0.66-1,1.0.69-0,1.0.69-1,1.0.69-2), while healthy long-running processes checked at thesame time used un-suffixed dirs (
1.0.69,1.0.70) - a 7/7 vs 2/2 correlation.4. An ETW CPU-sampling trace (
wpr.exe -start CPU -filemode, ~8s, analyzed withxperf -a profile -detail) targeting one stuck process showed its top CPU consumers as:ntoskrnl.exe(kernel)FLTMGR.SYS(filter manager)copilot.exe.oldNtfs.sysntdll.dllcldflt.sys(cloud-files/OneDrive filter),luafv.sys(UAC virtualization),bindflt.sys,fileinfo.sys,Wof.sysi.e. the hot samples land inside the renamed image itself, dominated by kernel and
filesystem-filter-driver time - consistent with a tight retry/poll loop making constant
filesystem syscalls (e.g. against the renamed binary, the pkg cache, or a file-watch path),
not a genuine idle wait.
5. Not tab/window related: the parent shells (
pwsh.exe/powershell.exe) hosting thesesessions were all still alive and responding, i.e. this isn't caused by a closed/detached
terminal tab - the CLI process itself is what's stuck.
Steps to reproduce
--plancopilotsession on Windows and leave itidle (no active turn) in a Windows Terminal tab.
days of uptime to hit naturally, or may be reproducible faster by forcing an update
cycle while a session is open).
copilot.exe.old(via
QueryFullProcessImageName/ PowerShell 7's(Get-Process -Id <pid>).ProcessName)and whether its CPU usage has pegged one core continuously (
Get-Process -Id <pid> | Select Threadsshows one thread stuck inRunningwith a fast-growingTotalProcessorTime).Expected behavior
Either the CLI detects that its own binary/runtime cache has been replaced and gracefully
exits or reloads, or - at minimum - it should never spin a thread at 100% while otherwise
idle. Idle CPU should be ~0% regardless of whether an update occurred underneath a
long-running session.
Workaround
I wrote a small, self-contained PowerShell script that detects and optionally kills these
stale/spinning processes (checks the live per-PID image name via
QueryFullProcessImageNameso it works correctly under both Windows PowerShell 5.1 and PowerShell 7, then optionally
re-samples CPU to confirm sustained spin before offering an interactive kill menu). Happy to
share it here or as a gist if useful for triage/repro.
Possibly related
launch_engine ACCESS_VIOLATION (0xC0000005) on Windows after auto-update replaces binary- samecopilot.exe/copilot.exe.oldrename-in-place mechanism, but adifferent symptom (crash on next launch vs. this issue's continuous spin on the
already-running old process).
instead of idle" symptom family, but on a different platform (WSL2, not Windows native)
and not tied to an update/rename event as far as I can tell from those reports, so likely
a distinct root cause.