Skip to content

Desktop/Server: Auto-updater and models.dev fetch lack network resilience (excessive errors in restricted networks) #35090

Description

@edisonzerolam

Environment

  • Version: v1.17.13
  • OS: Windows 10+ (restricted network, e.g. China mainland)
  • Components: electron-updater + server model fetch

Symptom 1: Auto-updater 10-minute polling storm

In a single 12-hour session, 50+ ERR_CONNECTION_TIMED_OUT / ERR_CONNECTION_RESET errors were logged.

Log pattern (repeating every 10 minutes):

updater state changed { from: 'up-to-date', to: 'checking' }
--- 20s network timeout ---
Error: Error: net::ERR_CONNECTION_TIMED_OUT
    at SimpleURLLoaderWrapper.<anonymous> ...
updater state changed { from: 'checking', to: 'error' }
--- wait 10 min ---
updater state changed { from: 'error', to: 'checking' }

Wasted time: ~18-25 minutes per 12h session (50 failures × ~22s each).

Why it happens

  1. Fixed 10-minute check interval regardless of success/failure
  2. TCP timeout for blocked connections is ~20s
  3. No exponential backoff or circuit breaker
  4. No network reachability pre-check before attempting

Symptom 2: models.dev fetch fails permanently

At every startup (across all logs examined):

[ERROR (#113)] Failed to fetch models.dev { cause: { failures: [...] } }

Why it happens

"Three-none design":

  1. No retry — one-shot call, failure is permanent until app restart
  2. No local cache — previously fetched model list is never persisted
  3. No fallback — no bundled default model list for cold start
  4. No mirror — no alternative endpoint if primary is unreachable

Shared root cause

Both the updater and model fetch assume global unrestricted Internet access. Their failure handling strategies are designed for "server busy" scenarios, not "network blocked" scenarios common in certain regions.

Suggested Fixes

P0 — Updater exponential backoff + timeout reduction

let failures = 0;
async function checkWithBackoff() {
  if (!await networkReachable(5000)) return;
  try {
    await updater.checkForUpdates({ timeout: 5000 });
    failures = 0;
  } catch {
    failures++;
    const nextInterval = Math.min(Math.pow(2, failures), 288) * 10 * 60 * 1000;  // max ~48h
    if (failures >= 3) cooldown(30 * 60 * 1000);  // circuit breaker
  }
}

P0 — Model fetch 4-layer defense

  1. Local cache: Persist fetched model list to models-cache.json in userData
  2. Online retry: 3 retries with backoff (0s → 5s → 30s) and 5s timeout
  3. Mirror fallback: Try alternative endpoints if primary fails
  4. Bundled defaults: Ship a models.default.json with common models for cold-start offline use

P1 — Proxy awareness

Detect system proxy settings (PAC/HTTP_PROXY env vars) and pass them to electron-updater and fetch calls. Many users in restricted networks rely on HTTP/SOCKS proxies.

P2 — Configurable update interval

Allow users to configure the update check interval or disable auto-updates entirely for offline use, rather than hardcoding 10 minutes.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions