Skip to content

Auto-update rewrites the copilot.exe it was launched from, breaking the GitHub Copilot app's bundled CLI #4728

Description

@doomslayer2k

Describe the bug

Running copilot in a terminal can silently break the GitHub Copilot desktop app. Afterwards the app cannot resume any existing session — every one fails with "Session unavailable — We couldn't reconnect to your session." Nothing in the app suggests the CLI is involved, and no session data is damaged.

The cause is that the auto-updater rewrites the copilot.exe it was launched from, in place. The desktop app pins its bundled engine by directory name (%LOCALAPPDATA%\github-copilot-sdk\cli\<version>\copilot.exe) and probes its version exactly once, at startup. When an unrelated invocation of that same binary auto-updates, the directory keeps its old version name while holding a newer build — and the app is left driving an engine whose RPC surface it no longer speaks.

The app cannot defend itself against this. It already launches the CLI with --no-auto-update; the damage is done by a different invocation, and the app has no way to know the file changed underneath it.

Frequency: deterministic. Once the binary is replaced, 100% of session resumes fail until it is restored.

Extensions: not a factor. Extensions do launch, then die as a consequence (peer-closed-before-ready, exit code=1) when the host tears the session down after the failed RPC.

Affected version

CLI after the auto-update 1.0.83-5
CLI the app had pinned 1.0.80
GitHub Copilot desktop app 1.1.14

The incompatibility itself is introduced somewhere in 1.0.81 — see the related issue below.

Steps to reproduce

  1. Install the GitHub Copilot desktop app (1.1.14). It bundles CLI 1.0.80 at %LOCALAPPDATA%\github-copilot-sdk\cli\1.0.80\copilot.exe.
  2. Start the app. It probes and records that binary once:
    INFO github_app::platform::health: Detected Copilot CLI version at startup
      version=Copilot binary version: 1.0.80 source=Some(Bundled)
      path="%LOCALAPPDATA%\github-copilot-sdk\cli\1.0.80\copilot.exe" bundled_version="1.0.80"
    
  3. From any other context, run that same copilot.exe without --no-auto-update (auto-update is on by default). In my case this was an ordinary terminal session — the CLI log shows "client_name": "github/cli", so it was not the app.
  4. Observe the updater replace the binary in place, preserving the old one alongside it:
    [INFO] Successfully downloaded package, restart to update to version 1.0.83-5
    [INFO] Downloading updated win32-x64 binary...
    
    The folder now contains copilot.exe (the new build) and copilot.exe.old-<pid>-<timestamp> (the original). The folder is still named 1.0.80.
  5. In the app, open any existing session.
  6. Observe "Session unavailable — We couldn't reconnect to your session."

Expected behavior

Auto-update should not rewrite a binary it did not install — or at minimum should not leave a versioned directory asserting a version its contents no longer are.

Additional context

Environment

App version         1.1.14
OS                  Windows 10 Enterprise 25H2 (build 26200.9106)
Architecture        AMD64
WebView2 runtime    152.0.4191.62
Copilot CLI         1.0.80          (after manual rollback; was 1.0.83-5 at failure)
Agency              2026.9.3.7
GPU 0               Intel(R) Graphics (32.0.101.8132)
GPU 1               NVIDIA GeForce RTX 5090 (32.0.16.1656)
WebView2 flags      --disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection,CalculateNativeWinOcclusion,UiaProvider,AccessibilityBlockFlowIterator
                    --force-renderer-accessibility

Related: github/app#3284 reports the same resume failure, but reached it deliberately via COPILOT_CLI_PATH — so that reporter could simply remove the override. This issue is the path with no opt-out: the bundled binary is the one replaced, so the documented fallback ("use the bundled CLI") is exactly what's broken. That issue is also filed on the app tracker, though the RPC surface is engine-owned, which may be why it hasn't been triaged.

Workaround (verified): set COPILOT_AUTO_UPDATE=false, then restore the preserved copilot.exe.old-<pid>-<timestamp> over copilot.exe. With the variable set, each launcher resolves its own built-in payload instead of the newest cached one, so independent installs stop interfering.

Suggested labels: whichever area:* covers packaging/updates, plus area:sessions for the user-visible symptom.

In-depth investigation

The failing call. The engine resumes fine; the app then makes one more call and gives up:

INFO  github_app::session::core: CLI session resumed
INFO  github_app::session::core: session resume flow completed
WARN  github_app::handlers::session: failed to resume session
      error=RPC error -32601: Unhandled method session.permissions.getAllowAll

What changed. Extracting session.permissions.* method strings from each engine's app.js:

Engine Methods exposed
1.0.80 14 — incl. getAllowAll, setAllowAll, setApproveAll, configure
1.0.83-5 1 — setMode

The app binary (1.1.14) contains 4 references to permissions.getAllowAll and zero to permissions.setMode, so it cannot drive the newer engine at all.

Why --no-auto-update doesn't save you. From the SEA loader, resolution order is: --prefer-versionCOPILOT_CLI_DIST_DIR → (auto-update on) newest downloaded pkg → (auto-update off) the binary's built-in version. So --no-auto-update pins to the binary file, not to a version number — which is precisely why replacing the file defeats it. Demonstrated on the same machine:

Invocation Reports
1.0.80 binary + --no-auto-update 1.0.80
1.0.80 binary, bare --version 1.0.83-5
1.0.83-5 binary + --no-auto-update 1.0.83-5

Note the middle row: --version alone is misleading, because auto-update is on for that invocation. --binary-version is the honest discriminator.

This is chronic. Agency keeps an update-check cache keyed by <path>|<version>. Decoding the entries for the app's single pinned path shows it has held at least five different builds:

…\cli\1.0.80\copilot.exe | 1.0.79
…\cli\1.0.80\copilot.exe | 1.0.80
…\cli\1.0.80\copilot.exe | 1.0.81-3
…\cli\1.0.80\copilot.exe | 1.0.82

(plus 1.0.83-5, the one that finally broke it). There is also a leftover cli\1.0.71\.copilot-update-<timestamp>\ staging directory from an earlier in-place update. The behaviour is long-standing; it only became visible when 1.0.81 removed the RPC method.

Blast radius. Only sessions needing a fresh engine process are affected — already-running ones keep the old mapped image and work normally. That makes it look intermittent, and it means restarting the app converts one broken session into all of them.

Activity

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:installationInstalling, updating, versioning, PATH setup, and binary distributionarea:sessionsSession management, resume, history, session picker, and session state

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions