{"record":{"id":"e0ba80bb02a65a59","repo":"CloakHQ/CloakBrowser","slug":"geoip-resolution-failed-could-not-discover-the-eg-e0ba80","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":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"dotnet/src/CloakBrowser/GeoIp.cs","lineNumber":120,"sourceCode":"        var dbPath = await EnsureGeoIpDbAsync(ct).ConfigureAwait(false);\n\n        var timeout = GetGeoIpTimeoutSeconds();\n        var deadline = DeadlineFromTimeout(timeout);\n\n        // Exit IP (through proxy, or the machine's own public IP when proxyUrl is\n        // null/empty) is most accurate - gateway DNS may differ from exit. Resolved\n        // even when the DB is unavailable: the IP does not need the DB, and dropping\n        // it on a DB hiccup would let WebRTC fall back to the real IP behind a proxy\n        // while the connection shows the proxy IP - a real deanonymization.\n        var ip = await ResolveExitIpAsync(proxyUrl, RemainingSeconds(deadline), ct).ConfigureAwait(false);\n        // Hostname fallback only applies to a proxy; no proxy -> echo services only.\n        if (ip == null && !string.IsNullOrEmpty(proxyUrl) && !DeadlineExpired(deadline))\n            ip = ResolveProxyIp(proxyUrl);\n        if (ip == null || DeadlineExpired(deadline))\n        {\n            if (deadline != null && DeadlineExpired(deadline))\n                throw new InvalidOperationException($\"GeoIP resolution timed out after {timeout:0.0}s\");\n            throw new InvalidOperationException(\"GeoIP resolution failed: could not discover the egress IP\");\n        }\n\n        if (dbPath == null)\n            throw new InvalidOperationException(\"GeoIP resolution failed: GeoIP database is unavailable\");\n\n        try\n        {\n            using var reader = new DatabaseReader(dbPath);\n            var resp = reader.City(ip);\n            var timezone = resp.Location?.TimeZone;\n            var country = resp.Country?.IsoCode;\n            string? locale = country != null && CountryLocaleMap.TryGetValue(country, out var l) ? l : null;\n            CloakLog.Debug(\"GeoIP: {0} -> tz={1}, country={2}, locale={3}\", ip, timezone, country, locale);\n            return (timezone, locale, ip);\n        }\n        catch (Exception exc)\n        {\n            throw new InvalidOperationException($\"GeoIP lookup failed for {ip}: {exc.Message}\", exc);","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/CloakHQ/CloakBrowser/blob/d6bad5de261bedf025280ace1d14e800aee13923/dotnet/src/CloakBrowser/GeoIp.cs#L102-L138","documentation":"The GeoIP resolver exhausted every egress-IP discovery strategy (echo services, and — when a proxy is configured — direct DNS resolution of the proxy hostname) without learning the public IP. Without an IP the MaxMind lookup cannot proceed, so the library throws this distinct 'discovery failed' error (as opposed to the timeout variant when a deadline expired).","triggerScenarios":"Calling ResolveProxyGeoAsync / MaybeResolveGeoIpAsync / Collect when: no echo service responds AND there is no proxyUrl, or the proxyUrl is empty, or ResolveProxyIp returned null (DNS failure / deadline already passed), and no deadline expiration is involved.","commonSituations":"Air-gapped or heavily firewalled networks blocking all IP echo endpoints; passing an empty or whitespace proxyUrl by mistake; offline development machines; DNS failures resolving the proxy host; IPv6-only environments unsupported by the echo services.","solutions":["Check network egress: curl an IP echo service from the same machine/proxy to confirm it is reachable.","Validate the proxyUrl string is a well-formed, non-empty URL before calling the GeoIP API.","Allow the necessary echo-service domains through the firewall/proxy, or provide the egress IP via the API that accepts one.","If GeoIP is optional for your flow, call the non-Geo path or disable GeoIP resolution."],"exampleFix":"// before\nvar geo = await browser.GeoIp.ResolveProxyGeoAsync(proxyUrl: \"\"); // no proxy, echo services blocked\n\n// after\nif (!string.IsNullOrWhiteSpace(proxyUrl) || await Network.CanReachEchoServiceAsync())\n    var geo = await browser.GeoIp.ResolveProxyGeoAsync(proxyUrl);\nelse\n    var geo = GeoResult.Unknown; // degrade gracefully instead of throwing","handlingStrategy":"fallback","validationCode":"bool canDiscover = !string.IsNullOrWhiteSpace(proxyUrl) || await Network.AnyEchoServiceReachableAsync();\nif (!canDiscover) geo = GeoResult.Unknown; // skip resolution","typeGuard":null,"tryCatchPattern":"catch (InvalidOperationException ex) when (ex.Message.Contains(\"could not discover the egress IP\"))\n{\n    geo = GeoResult.Unknown; // degrade gracefully: proceed without geo data\n}","preventionTips":["Validate proxyUrl is non-empty and well-formed before calling Geo APIs.","Firewall-allow the IP echo endpoints the library uses, or supply the egress IP directly.","Treat GeoIP as optional in locked-down environments and design a no-geo fallback path."],"tags":["geoip","network","egress-ip","proxy"],"backgroundTag":"egress-ip-discovery-failed","analyzedSha":"d6bad5de261bedf025280ace1d14e800aee13923","analyzedAt":"2026-08-28T14:13:12.918Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}