{"record":{"id":"838de3d2ff74a17f","repo":"agalwood/Motrix","slug":"geoipdownloadfailed","errorCode":"GeoIPDownloadFailed","errorMessage":"network error: ${(err as Error).message}","messagePattern":"network error: (.+?)","errorType":"error_code","errorClass":"AppError","httpStatus":null,"severity":"error","filePath":"src/core/geoip/geo-ip-downloader.ts","lineNumber":59,"sourceCode":"    onProgress?: ProgressListener\n  ): Promise<DownloadResult> {\n    await mkdir(path.dirname(dbPath), { recursive: true })\n\n    const controller = new AbortController()\n    const timeoutId = setTimeout(\n      () => controller.abort(),\n      this.options.timeoutMs\n    )\n\n    let response: Response\n    try {\n      response = await fetch(url, {\n        signal: controller.signal,\n        redirect: 'follow',\n      })\n    } catch (err) {\n      clearTimeout(timeoutId)\n      throw new AppError(\n        ErrorCode.GeoIPDownloadFailed,\n        `network error: ${(err as Error).message}`,\n        err\n      )\n    }\n\n    if (!response.ok) {\n      clearTimeout(timeoutId)\n      throw new AppError(\n        ErrorCode.GeoIPDownloadFailed,\n        `http ${response.status} ${response.statusText} for ${url}`\n      )\n    }\n\n    const totalHeader = response.headers.get('content-length')\n    const bytesTotal = totalHeader ? Number.parseInt(totalHeader, 10) : -1\n    const version = deriveVersion(response.headers)\n","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/geoip/geo-ip-downloader.ts#L41-L77","documentation":"Thrown by the GeoIP database downloader when the underlying fetch() rejects. Any network-layer failure — DNS resolution error, connection refused/reset, TLS handshake failure, or an AbortController timeout firing — surfaces here as GeoIPDownloadFailed with the original error chained as the cause. The downloader treats the request as never having reached a valid HTTP response.","triggerScenarios":"fetch(url, { signal, redirect: 'follow' }) throws. Specifically: DNS failure on the GeoIP CDN host; TCP connection refused/reset; TLS certificate error; the timeout set via setTimeout+controller.abort() fires before a response; the user is offline.","commonSituations":"No internet connectivity; corporate proxy/MITM blocking the CDN; the GeoIP host DNS is misconfigured; timeoutMs is set too low for a slow connection; an expired/blocked CA chain causes TLS failure; firewall egress blocked.","solutions":["Verify connectivity to the configured GeoIP download URL (curl -I <url>) from the same host.","Increase options.timeoutMs to accommodate slow links.","Check DNS resolution for the host and fix /etc/hosts or resolver config if it fails.","If behind a proxy, ensure HTTP(S)_PROXY env vars are set so fetch uses them.","Retry the update — transient network blips are common and the manager supports re-running runUpdate()."],"exampleFix":"// before\ntry {\n  response = await fetch(url, { signal: controller.signal, redirect: 'follow' })\n} catch (err) {\n  throw new AppError(ErrorCode.GeoIPDownloadFailed, `network error: ${(err as Error).message}`, err)\n}\n\n// after — distinguish abort/timeout from genuine network errors for the caller\n} catch (err) {\n  clearTimeout(timeoutId)\n  const aborted = (err as Error).name === 'AbortError'\n  throw new AppError(\n    ErrorCode.GeoIPDownloadFailed,\n    aborted ? `download timed out after ${this.options.timeoutMs}ms` : `network error: ${(err as Error).message}`,\n    err\n  )\n}","handlingStrategy":"retry","validationCode":"// Pre-flight connectivity check (best-effort; not authoritative).\nasync function geoIpReachable(url: string, timeoutMs: number): Promise<boolean> {\n  try {\n    const ctrl = new AbortController()\n    const t = setTimeout(() => ctrl.abort(), timeoutMs)\n    await fetch(url, { method: 'HEAD', signal: ctrl.signal })\n    clearTimeout(t)\n    return true\n  } catch { return false }\n}","typeGuard":null,"tryCatchPattern":"for (const delay of [0, 1000, 5000]) {\n  try {\n    return await downloader.download(...)\n  } catch (err) {\n    if (err instanceof AppError && err.code === ErrorCode.GeoIPDownloadFailed && /network error/.test(err.message) && delay < 5000) {\n      await new Promise(r => setTimeout(r, delay))\n      continue\n    }\n    throw err\n  }\n}","preventionTips":["Configure reasonable timeoutMs (e.g. 30s+) for slow links.","Ensure HTTP(S)_PROXY env vars are set behind corporate proxies.","Validate DNS for the GeoIP host before scheduling updates.","Schedule retries with backoff for transient network errors."],"tags":["geoip","network","download","fetch","timeout"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}