CloakHQ/CloakBrowser · error · InvalidOperationException

GeoIP resolution timed out after {timeout:0.0}s

Error message

GeoIP resolution timed out after {timeout:0.0}s

What it means

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.

Source

Thrown at dotnet/src/CloakBrowser/GeoIp.cs:119

        // timeout (a first-use ~70MB fetch legitimately outlasts it).
        var dbPath = await EnsureGeoIpDbAsync(ct).ConfigureAwait(false);

        var timeout = GetGeoIpTimeoutSeconds();
        var deadline = DeadlineFromTimeout(timeout);

        // Exit IP (through proxy, or the machine's own public IP when proxyUrl is
        // null/empty) is most accurate - gateway DNS may differ from exit. Resolved
        // even when the DB is unavailable: the IP does not need the DB, and dropping
        // it on a DB hiccup would let WebRTC fall back to the real IP behind a proxy
        // while the connection shows the proxy IP - a real deanonymization.
        var ip = await ResolveExitIpAsync(proxyUrl, RemainingSeconds(deadline), ct).ConfigureAwait(false);
        // Hostname fallback only applies to a proxy; no proxy -> echo services only.
        if (ip == null && !string.IsNullOrEmpty(proxyUrl) && !DeadlineExpired(deadline))
            ip = ResolveProxyIp(proxyUrl);
        if (ip == null || DeadlineExpired(deadline))
        {
            if (deadline != null && DeadlineExpired(deadline))
                throw new InvalidOperationException($"GeoIP resolution timed out after {timeout:0.0}s");
            throw new InvalidOperationException("GeoIP resolution failed: could not discover the egress IP");
        }

        if (dbPath == null)
            throw new InvalidOperationException("GeoIP resolution failed: GeoIP database is unavailable");

        try
        {
            using var reader = new DatabaseReader(dbPath);
            var resp = reader.City(ip);
            var timezone = resp.Location?.TimeZone;
            var country = resp.Country?.IsoCode;
            string? locale = country != null && CountryLocaleMap.TryGetValue(country, out var l) ? l : null;
            CloakLog.Debug("GeoIP: {0} -> tz={1}, country={2}, locale={3}", ip, timezone, country, locale);
            return (timezone, locale, ip);
        }
        catch (Exception exc)
        {

View on GitHub (pinned to d6bad5de26)

Solutions

  1. Increase the GeoIP resolution timeout/deadline passed to the API.
  2. Verify the proxy URL is reachable and that IP echo services (e.g. api.ipify-style endpoints) are not firewalled.
  3. Pre-resolve or cache the egress IP and pass it in (or skip GeoIP) when operating in restricted networks.
  4. If DNS on the proxy host is the bottleneck, warm it up or pass a pre-resolved IP.

Example fix

// before
var geo = await browser.GeoIp.ResolveProxyGeoAsync(proxyUrl, timeout: TimeSpan.FromSeconds(1));

// after
var geo = await browser.GeoIp.ResolveProxyGeoAsync(proxyUrl, timeout: TimeSpan.FromSeconds(10));
// or skip geo resolution entirely on locked-down networks
// var geo = await browser.GeoIp.ResolveProxyGeoAsync(proxyUrl, timeout: Timeout.InfiniteTimeSpan, skipOnTimeout: true);
Defensive patterns

Strategy: retry

Validate before calling

var sw = Stopwatch.StartNew();
if (!await Network.CanReachAsync("https://api.ipify.org"))
    geoTimeout = TimeSpan.FromSeconds(30); // be generous on restricted networks

Try / catch

catch (InvalidOperationException ex) when (ex.Message.Contains("GeoIP resolution timed out"))
{
    geo = await browser.GeoIp.ResolveProxyGeoAsync(proxyUrl, timeout: TimeSpan.FromSeconds(30)); // retry with a larger budget
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of CloakHQ/CloakBrowser@d6bad5de26 (2026-08-28). Data as JSON: /api/errors/7f0f302967bab80d. Report an issue: GitHub.