[Bug] Transient network loss is recorded as a persistent install_failed; "Auto-update failed" banner never clears after reconnect (Windows native, already on latest)

Status Open
Reported on v2.1.147
Maintainer reply None cached
Activity 4 comments · opened Jun 3, 2026

Preflight Checklist

  • [x] I have searched existing issues and this specific combination hasn't been reported. Related: #62130 (same root cause: a transient check failure surfaced as an install failure on an already-current install), #64554 / #64292 (doctor/banner mismatch; #64292 also shows a network socket error), #61676 (Windows auto-updater regression from the 2.1.147 rework), #56189 (persistent un-suppressible banner).
  • [x] This is a single bug report.
  • [x] I am using the latest version of Claude Code.

What's Wrong?

If network connectivity drops while a Claude Code session is running (e.g. Disable the Ethernet adapter, Windows 10), the background auto-update check fails and the CLI shows:

✗ Auto-update failed · Run /doctor

The problem is not the failure itself but that the failed state is sticky:

  1. The banner never clears after connectivity is restored — it keeps appearing on every startup.
  2. Running /doctor does not clear it; /doctor reports Last update attempt: failed (install_failed).
  3. Crucially, the install is a healthy native install already on the latest channel version (Currently running: native (2.1.161), Latest version: 2.1.161, channel latest). There was nothing to install — so a transient network/check failure is being (a) misclassified as install_failed and (b) persisted indefinitely without ever being reset on the next successful check or when the network comes back.

This is the same root cause as #62130 (transient check surfaced as install failure) but on Windows native, with a clean deterministic trigger and the added problem that the failed state is persisted and never reset.

What Should Happen?

  • Distinguish a transient network / check failure from a real install failure (cf. #62130); a benign check failure should retry silently, not raise the red banner.
  • When the installed version already equals the channel target (nothing to do), never show "Auto-update failed".
  • The failed state must be reset on the next successful check / when connectivity returns, and /doctor should clear it (or at least offer an action to re-check), so the banner does not persist forever.

Error Messages/Logs

/doctor output (sanitized) while the banner was stuck, after the network had been restored:

Diagnostics
Currently running: native (2.1.161)
Commit: 6a550aea7c74
Platform: win32-x64
Config install method: native

Updates
Auto-updates: enabled
Auto-update channel: latest
Last update attempt: failed (install_failed) — 2026-06-03
Stable version: 2.1.150
Latest version: 2.1.161

Note: Currently running already equals Latest version (2.1.161), so no install was actually pending — yet the last attempt is recorded as install_failed.

Steps to Reproduce

  1. Native install on Windows 10, auto-update channel latest, already on the latest published version.
  2. Start a Claude Code session.
  3. While the session is running, disable the network adapter (e.g. Disable the Ethernet NIC).
  4. On the next background update cycle the banner ✗ Auto-update failed · Run /doctor appears.
  5. Re-enable the network adapter → the banner stays.
  6. Run /doctor → it reports Last update attempt: failed (install_failed) and does not clear the state; the banner keeps appearing on every startup.

Is this a regression?

Yes (perceived) — this did not happen on earlier versions; it started a few releases ago. Timing is consistent with the auto-updater rework noted in the 2.1.147 changelog and in #61676.

Last Working Version

Unknown (a few versions ago).

Claude Code Version

2.1.161 (Claude Code)

Platform

Claude Pro/Max (CLI)

Operating System

Windows 10 Pro 10.0.19045 (win32-x64)

Terminal/Shell

PowerShell

Additional Information

Reproducible on demand by toggling the NIC, which makes this a clean repro for the "transient check failure persisted as install_failed" class of bugs (#62130, #64554, #64292).

View original on GitHub ↗

3 Comments

Tatendaz · 2 months ago

Confirming on macOS (darwin-arm64) — and a data point that rules network out as the trigger

Seeing this exact sticky install_failed banner on macOS native, so it isn't Windows-specific. More importantly, in my case there is no network involvement at all — it reproduces on a healthy connection, which suggests the "transient network/check failure" framing is too narrow: there appears to be a code path that throws on the already-on-latest run itself.

Environment

  • Claude Code 2.1.177 (native installer), darwin-arm64, macOS 26.5.1 (build 25F80 / Darwin 25.5.0)
  • Channel latest; Currently running == Latest version == 2.1.177 (nothing to install)
  • installMethod: native, autoUpdatesProtectedForNative: true. Independent of the autoUpdates flag — it failed identically while that flag was false (6-13, 6-14) and after flipping it to true (6-15).

Symptom — identical to this issue: ✗ Auto-update failed · Run /doctor, sticky across restarts.

It recurs ~once per day, deterministically, on a full connection. ~/.claude/.last-update-result.json was rewritten on three consecutive days, byte-identical except the timestamp:

{"path":"native","outcome":"failed","status":"install_failed","version_from":"2.1.177","version_to":null,"error_code":null}

(2026-06-13, -14, -15 — all while already on 2.1.177, the latest.)

What I ruled out (so it's not environment, and not network):

  • The latest channel and …/2.1.177/manifest.json both fetch fine (HTTP 200); the published darwin-arm64 size/checksum match the installed binary exactly.
  • Plenty of free disk; versions/, staging/, and the lock dir are all clean (staging empty, no stale locks).
  • Manual claude update succeeds every timeClaude Code is up to date (2.1.177), and notably does not write .last-update-result.json.

Root-cause mechanism (from the 2.1.177 bundle). The native updater records its result like this:

// happy path
if (G.wasUpdated)  huH({ outcome:"success", status:"success", version_to:G.latestVersion })
else               /* tengu_native_auto_updater_up_to_date — writes NO result file */
// failure path
catch (G) {
  N(`Native auto-updater failed: ${G.message}`, { level:"error" })
  huH({ outcome:"failed", status:"install_failed", version_from:<cur>, version_to:null, error_code:null })
}

Two things fall out of this and together explain the "never clears" behavior:

  1. The clean already-up-to-date path writes no result file at all, so it can never overwrite a prior install_failed. Any earlier failure is sticky until a real update lands or the file is removed by hand. (This is the persistence half of the bug.)
  2. In my case the updater is hitting the catch on an already-latest run — version_to: null and error_code: null (so it threw before resolving a target, and it's none of the categorized cases: exe-lock / restore-failed / env-setup). The thrown message only goes to N(…, {level:"error"}) and isn't persisted anywhere I could find, so I can't capture the exact exception from outside the process — but with network/manifest/disk/locks all healthy and manual update working, the throw looks intrinsic to the background already-latest path rather than a transient external failure.

Net: the two fixes requested in this issue would cover the macOS case too — (a) never record/show install_failed when current == latest (nothing to install), and (b) reset the persisted state on the next successful or up-to-date check, not only on a real update. It would also help to surface that caught Native auto-updater failed: … message in a durable debug log so the underlying throw can actually be diagnosed.

Happy to provide /doctor output or anything else useful.

lukeme117 · 1 month ago

Confirming the same misclassification + never-reset behavior on Linux (no network loss involved) — this is not Windows- or connectivity-specific.

Environment

  • Debian, kernel 6.12.94+deb13-amd64
  • Native installer (~/.local/share/claude/versions/, symlink from ~/.local/bin/claude), channel latest
  • Note: this box routinely runs several concurrent Claude Code sessions, so background update checks fire more often than a typical single-session setup

Symptom
/doctor has shown Last update attempt: failed (install_failed) persistently across many versions (first noticed around 2.1.191), while updates actually land successfully every day:

~/.local/share/claude/versions/
  2.1.196  (installed 2026-06-30)
  2.1.197  (installed 2026-07-01)
  2.1.198  (installed 2026-07-02 05:00, symlink flipped 05:04)

Staging dir clean, no locks, symlink correct — install is healthy and on latest.

The stale record
~/.claude/.last-update-result.json at the time /doctor reported the failure:

{"timestamp":"2026-07-02T08:17:05.522Z","path":"native","outcome":"failed","status":"install_failed","version_from":"2.1.198","version_to":null,"error_code":null}

version_from equals the installed latest and version_to is null — i.e. a background check that resolved no target version (already up to date) was recorded as install_failed. No connectivity outage occurred here, so the trigger differs from the OP's repro, but the failure mode is the same: a check-phase no-op/transient is persisted as an install failure and never reconciled.

Manual path doesn't clear it
claude updateClaude Code is up to date (2.1.198), exits clean — and does not rewrite .last-update-result.json, so the stale failed record survives and /doctor keeps displaying it indefinitely.

Suggests the fix shape in the OP holds generally: treat version_from == latest && version_to == null as success/no-op, and reset the persisted state on any successful check (including the manual up-to-date path).

g-i-o-r-g-i-o · 1 month ago

Still reproducing on 2.1.220 — Windows 10 Pro 10.0.19045 (win32-x64), native install, channel latest.

Almost two months and many releases after the original report, the signature is unchanged. Current content of ~/.claude/.last-update-result.json:

{"timestamp":"2026-07-30T18:12:39.840Z","path":"native","outcome":"failed","status":"install_failed","version_from":"2.1.220","version_to":null,"error_code":null}

version_to: null confirms no install was ever attempted: the check failed (network dropped while the session was running) and the outcome was recorded as install_failed. 2.1.220 is already the current version, so there was nothing to install in the first place.

Once that record is written it is never reset — the banner survives reconnection, restarts and subsequent successful checks. Neither claude doctor nor claude update clears it; the only way out is deleting ~/.claude/.last-update-result.json by hand.

Newer reports describing the same behaviour: #81898 (false banner on 2.1.220 while already up to date), #82408 (claude update does not clear the stale record, and claude doctor never reads that file), #70608 (failed state never self-heals) — in addition to #62130 / #64554 / #64292 already linked in the description.

The fix asked for in the original report still stands: do not persist a failed check as install_failed, and reset the record on the next successful check.

Showing cached comments. Read the full discussion on GitHub ↗