Native updater hangs indefinitely (30+ min at 0 bytes) on stalled download — no stall timeout or retry

Open 💬 0 comments Opened Jul 13, 2026 by BeheSQ

Summary

The native installer's self-update (claude update / auto-update) hung for 30+ minutes at 0 bytes downloaded on a stalled connection to the release CDN, with no stall timeout, no error, and no retry. Killing and rerunning the command immediately worked (a later retry completed the same 241 MB download in a few minutes).

Environment

  • Claude Code 2.1.204 updating to 2.1.207, native install (~/.local/bin/claude~/.local/share/claude/versions/…)
  • macOS Darwin 25.6.0 (Apple Silicon)
  • No proxy, macOS application firewall disabled, 200+ GB free disk

What happened

  • claude update (and the in-session auto-update) sat at 0 bytes downloaded for ~30 minutes with no progress, no timeout, and no failure message.
  • The user gave up, downloaded the binary manually, and attempted a manual install; meanwhile one of the claude update retries succeeded on a fresh connection — ~/.claude/.last-update-result.json recorded outcome: success, 2.1.204 → 2.1.207.

Diagnosis (done after the fact)

Everything on the machine checks out, which points to a transient hung TCP connection (connected, but zero data flowing — e.g. mid-path/IPv6 blackhole):

  • DNS for storage.googleapis.com resolves fine (v4 + v6); both curl -4 and curl -6 reach it in <0.5 s.
  • A ranged 20 MB download of claude-code-releases/2.1.207/darwin-arm64/claude succeeds (~1.3 MB/s).
  • claude update completes normally now.

So the trigger was transient network, but the updater turned a seconds-scale hiccup into a 30-minute silent hang.

Expected behavior

The updater's download should:

  1. Abort a stalled transfer quickly — e.g. a low-speed limit (< 1 KB/s for 30 s → abort), the equivalent of curl's --speed-time/--speed-limit.
  2. Retry with a fresh connection (ideally alternating address family, since a v6 blackhole with working v4 is a common cause of exactly this signature).
  3. Surface progress/failure — after N failed attempts, print an actionable error instead of hanging silently.
  4. Optionally resume with a Range request instead of restarting a 241 MB download from zero.

Workaround

Ctrl-C and rerun claude update — a fresh connection unsticks it.

View original on GitHub ↗