{"record":{"id":"f942db9bbea8ec7a","repo":"CloakHQ/CloakBrowser","slug":"geoip-resolution-failed-could-not-discover-the-eg","errorCode":null,"errorMessage":"GeoIP resolution failed: could not discover the egress IP","messagePattern":"GeoIP resolution failed: could not discover the egress IP","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"js/src/geoip.ts","lineNumber":114,"sourceCode":"  // timeout (a first-use ~70MB fetch legitimately outlasts it).\n  const dbPath = await ensureGeoipDb();\n\n  const timeoutMs = getGeoipTimeoutMs();\n  const deadline = deadlineFromTimeout(timeoutMs);\n\n  // Exit IP (through proxy, or the machine's own public IP when proxyUrl is\n  // falsy) is most accurate — gateway DNS may differ from exit. Resolved even\n  // when the DB is unavailable: the IP does not need the DB, and dropping it on\n  // a DB hiccup would let WebRTC fall back to the real IP behind a proxy while\n  // the connection shows the proxy IP — a real deanonymization.\n  let ip = await resolveExitIp(proxyUrl, remainingMs(deadline));\n  // Hostname fallback only applies to a proxy; no proxy → echo services only\n  if (!ip && proxyUrl && !deadlineExpired(deadline)) ip = await resolveProxyIp(proxyUrl);\n  if (!ip || deadlineExpired(deadline)) {\n    if (deadlineExpired(deadline)) {\n      throw new Error(`GeoIP resolution timed out after ${timeoutMs / 1000}s`);\n    }\n    throw new Error(\"GeoIP resolution failed: could not discover the egress IP\");\n  }\n\n  if (!dbPath) {\n    throw new Error(\"GeoIP resolution failed: GeoIP database is unavailable\");\n  }\n\n  try {\n    const buf = fs.readFileSync(dbPath);\n    const reader = new Reader(buf);\n    const result = reader.get(ip) as any;\n    const timezone: string | null = result?.location?.time_zone ?? null;\n    const countryCode: string | null = result?.country?.iso_code ?? null;\n    const locale =\n      countryCode ? (COUNTRY_LOCALE_MAP[countryCode] ?? null) : null;\n    return { timezone, locale, exitIp: ip };\n  } catch (error) {\n    const detail = error instanceof Error ? error.message : String(error);\n    throw new Error(`GeoIP lookup failed for ${ip}: ${detail}`, { cause: error });","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/CloakHQ/CloakBrowser/blob/d6bad5de261bedf025280ace1d14e800aee13923/js/src/geoip.ts#L96-L132","documentation":"resolveProxyGeo could not determine the egress IP: neither echo services nor proxy hostname resolution returned an IP before the deadline. The library throws it because all subsequent GeoIP lookups require a concrete egress IP to map to a country/timezone.","triggerScenarios":"Calling resolveProxyGeo (directly or via maybeResolveGeoip) with no reachable echo service, a proxyUrl whose hostname fails DNS resolution, or a deadline that expired mid-discovery (env CLOAKBROWSER_GEOIP_TIMEOUT_SECONDS too small).","commonSituations":"Offline/sandboxed environments blocking echo endpoints, an unreachable or misconfigured proxy (typo'd host, dead upstream), DNS failures inside containers, or aggressive timeouts on slow proxy connections.","solutions":["Check network egress and DNS: curl an echo service (e.g. ifconfig.me) from the same host/container","Verify the proxy URL is reachable and its hostname resolves; test with curl -x <proxyUrl> https://ifconfig.me","Increase CLOAKBROWSER_GEOIP_TIMEOUT_SECONDS so proxy hostname resolution fits in the deadline","Skip GeoIP resolution and pass timezone/locale explicitly if you already know them"],"exampleFix":"// before\nconst geo = await resolveProxyGeo({ proxyUrl, dbPath });\n\n// after\nconst geo = await resolveProxyGeo({\n  proxyUrl,\n  dbPath,\n  timeoutMs: 30_000,\n}).catch((err) => {\n  if (err.message.includes('egress IP')) {\n    return { timezone: 'UTC', locale: 'en-US', exitIp: null };\n  }\n  throw err;\n});","handlingStrategy":"fallback","validationCode":"async function canReachEgress(proxyUrl?: string): Promise<boolean> {\n  try {\n    const url = 'https://ifconfig.me';\n    const res = await fetch(url, proxyUrl ? { agent: new ProxyAgent(proxyUrl) } : {});\n    return res.ok;\n  } catch { return false; }\n}","typeGuard":"function isEgressDiscoveryError(e: unknown): e is Error {\n  return e instanceof Error && e.message.includes('could not discover the egress IP');\n}","tryCatchPattern":"try {\n  geo = await resolveProxyGeo({ proxyUrl, dbPath });\n} catch (e) {\n  if (isEgressDiscoveryError(e)) geo = { timezone: 'UTC', locale: 'en-US', exitIp: null };\n  else throw e;\n}","preventionTips":["Pre-flight an echo-service reachability check before launching the browser","Validate proxyUrl with a quick DNS lookup of its hostname","Set CLOAKBROWSER_GEOIP_TIMEOUT_SECONDS generously for slow proxies","Supply explicit timezone/locale so GeoIP is optional"],"tags":["geoip","network","proxy","dns"],"backgroundTag":"egress-ip-discovery-failed","analyzedSha":"d6bad5de261bedf025280ace1d14e800aee13923","analyzedAt":"2026-08-28T14:13:12.918Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}