{"record":{"id":"7f0f302967bab80d","repo":"CloakHQ/CloakBrowser","slug":"geoip-resolution-timed-out-after-timeout-0-0-s","errorCode":null,"errorMessage":"GeoIP resolution timed out after {timeout:0.0}s","messagePattern":"GeoIP resolution timed out after (.+?)s","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"dotnet/src/CloakBrowser/GeoIp.cs","lineNumber":119,"sourceCode":"        // timeout (a first-use ~70MB fetch legitimately outlasts it).\n        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        {","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/CloakHQ/CloakBrowser/blob/d6bad5de261bedf025280ace1d14e800aee13923/dotnet/src/CloakBrowser/GeoIp.cs#L101-L137","documentation":"The GeoIP resolver could not determine the proxy/browser egress IP before the caller-supplied deadline elapsed. Because all downstream GeoIP lookups need an IP, the library aborts with a timeout error. It is a wrapper condition: any slow echo-service or DNS resolution that overshoots the deadline triggers it.","triggerScenarios":"Calling ResolveProxyGeoAsync / MaybeResolveGeoIpAsync / Collect with a short timeout (the deadline param) while the egress-IP echo services or ResolveProxyIp(proxyUrl) DNS lookup are slow, blocked, or the proxy is unreachable — DeadlineExpired(deadline) is true after the discovery attempts.","commonSituations":"Corporate firewalls blocking IP echo endpoints; slow or dead proxies making every probe hang until the deadline; timeouts tuned too aggressively (sub-second); IPv6-only environments where echo services fail; captive-portal networks.","solutions":["Increase the GeoIP resolution timeout/deadline passed to the API.","Verify the proxy URL is reachable and that IP echo services (e.g. api.ipify-style endpoints) are not firewalled.","Pre-resolve or cache the egress IP and pass it in (or skip GeoIP) when operating in restricted networks.","If DNS on the proxy host is the bottleneck, warm it up or pass a pre-resolved IP."],"exampleFix":"// before\nvar geo = await browser.GeoIp.ResolveProxyGeoAsync(proxyUrl, timeout: TimeSpan.FromSeconds(1));\n\n// after\nvar geo = await browser.GeoIp.ResolveProxyGeoAsync(proxyUrl, timeout: TimeSpan.FromSeconds(10));\n// or skip geo resolution entirely on locked-down networks\n// var geo = await browser.GeoIp.ResolveProxyGeoAsync(proxyUrl, timeout: Timeout.InfiniteTimeSpan, skipOnTimeout: true);","handlingStrategy":"retry","validationCode":"var sw = Stopwatch.StartNew();\nif (!await Network.CanReachAsync(\"https://api.ipify.org\"))\n    geoTimeout = TimeSpan.FromSeconds(30); // be generous on restricted networks","typeGuard":null,"tryCatchPattern":"catch (InvalidOperationException ex) when (ex.Message.Contains(\"GeoIP resolution timed out\"))\n{\n    geo = await browser.GeoIp.ResolveProxyGeoAsync(proxyUrl, timeout: TimeSpan.FromSeconds(30)); // retry with a larger budget\n}","preventionTips":["Size the GeoIP deadline relative to worst-case echo-service latency (>=5-10s on proxied networks).","Verify proxy reachability before starting GeoIP resolution.","Cache the resolved egress IP across calls when the proxy is stable."],"tags":["geoip","timeout","network","proxy","dns"],"backgroundTag":"operation-timed-out","analyzedSha":"d6bad5de261bedf025280ace1d14e800aee13923","analyzedAt":"2026-08-28T14:13:12.918Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}