{"record":{"id":"6941d66b5e5a3ee9","repo":"CloakHQ/CloakBrowser","slug":"geoip-resolution-failed-geoip-database-is-unavail","errorCode":null,"errorMessage":"GeoIP resolution failed: GeoIP database is unavailable","messagePattern":"GeoIP resolution failed: GeoIP database is unavailable","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"js/src/geoip.ts","lineNumber":118,"sourceCode":"  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 });\n  }\n}\n\nfunction getGeoipTimeoutMs(): number {","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/CloakHQ/CloakBrowser/blob/d6bad5de261bedf025280ace1d14e800aee13923/js/src/geoip.ts#L100-L136","documentation":"An egress IP was discovered, but no MaxMind GeoIP database file path was supplied or found, so the lookup cannot proceed. The library throws because the offline Reader requires a local .mmdb file.","triggerScenarios":"Calling resolveProxyGeo without dbPath (or with an empty path) when the GeoIP database has not been pre-downloaded, e.g. downloadGeoipDb was never run or its result was not wired into the call.","commonSituations":"Fresh installs that skipped the DB download step, CI pipelines that do not cache the .mmdb artifact, or a download that failed silently earlier leaving no file path.","solutions":["Run downloadGeoipDb(dest) at startup and persist the resulting .mmdb path","Pass the resolved dbPath into resolveProxyGeo / maybeResolveGeoip options","Cache the downloaded database in CI or bundle it in the image so dbPath always exists","Fall back to explicit timezone/locale when the DB cannot be provisioned"],"exampleFix":"// before\nconst geo = await resolveProxyGeo({ ip }); // no dbPath\n\n// after\nconst dbPath = await ensureGeoipDb(); // downloads once, caches path\nconst geo = await resolveProxyGeo({ ip, dbPath });","handlingStrategy":"validation","validationCode":"import fs from 'node:fs';\nfunction assertGeoipDb(dbPath?: string): string {\n  if (!dbPath || !fs.existsSync(dbPath) || fs.statSync(dbPath).size === 0) {\n    throw new Error('GeoIP DB missing — run downloadGeoipDb first');\n  }\n  return dbPath;\n}","typeGuard":"function hasGeoipDb(dbPath?: string | null): dbPath is string {\n  return typeof dbPath === 'string' && dbPath.length > 0;\n}","tryCatchPattern":"try { geo = await resolveProxyGeo({ ip, dbPath }); }\ncatch (e) {\n  if (e instanceof Error && e.message.includes('database is unavailable')) {\n    await downloadGeoipDb(dest); geo = await resolveProxyGeo({ ip, dbPath: dest });\n  } else throw e;\n}","preventionTips":["Bootstrap downloadGeoipDb at service startup before any GeoIP-dependent call","Cache the .mmdb path in config rather than passing undefined","Add a CI step verifying the DB file exists and is non-empty"],"tags":["geoip","maxmind","configuration","missing-file"],"backgroundTag":"geoip-database-missing","analyzedSha":"d6bad5de261bedf025280ace1d14e800aee13923","analyzedAt":"2026-08-28T14:13:12.918Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}