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
- Fixed 10-minute check interval regardless of success/failure
- TCP timeout for blocked connections is ~20s
- No exponential backoff or circuit breaker
- 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":
- No retry — one-shot call, failure is permanent until app restart
- No local cache — previously fetched model list is never persisted
- No fallback — no bundled default model list for cold start
- 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
- Local cache: Persist fetched model list to
models-cache.json in userData
- Online retry: 3 retries with backoff (0s → 5s → 30s) and 5s timeout
- Mirror fallback: Try alternative endpoints if primary fails
- 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.
Environment
Symptom 1: Auto-updater 10-minute polling storm
In a single 12-hour session, 50+
ERR_CONNECTION_TIMED_OUT/ERR_CONNECTION_RESETerrors were logged.Log pattern (repeating every 10 minutes):
Wasted time: ~18-25 minutes per 12h session (50 failures × ~22s each).
Why it happens
Symptom 2: models.dev fetch fails permanently
At every startup (across all logs examined):
Why it happens
"Three-none design":
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
P0 — Model fetch 4-layer defense
models-cache.jsonin userDatamodels.default.jsonwith common models for cold-start offline useP1 — 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.