decolua/9router · warning

[ProxyFetch] Proxy failed, falling back to direct: ${proxyEr

Error message

[ProxyFetch] Proxy failed, falling back to direct: ${proxyError.message}

What it means

In the got-scraping code path, the proxied fetch is attempted first; on failure (and when strictProxy is not true) it logs this warning and re-issues the request with plain native fetch without the dispatcher, i.e. direct connection. Like the dispatcher path, strictProxy=true turns the proxy failure into a thrown error instead.

Source

Thrown at open-sse/utils/proxyFetch.js:346

    try {
      const parsedUrl = new URL(targetUrl);
      const realIP = await resolveRealIP(parsedUrl.hostname);
      if (realIP) return await createBypassRequest(parsedUrl, realIP, options);
    } catch (error) {
      console.warn(`[ProxyFetch] MITM bypass failed: ${error.message}`);
    }
  }

  if (proxyUrl) {
    try {
      const dispatcher = await getDispatcher(proxyUrl);
      return await originalFetch(url, { ...options, dispatcher });
    } catch (proxyError) {
      // If strictProxy is enabled, fail hard instead of falling back to direct
      if (proxyOptions?.strictProxy === true) {
        throw new Error(`[ProxyFetch] Proxy required but failed (strictProxy=true): ${proxyError.message}`);
      }
      console.warn(`[ProxyFetch] Proxy failed, falling back to direct: ${proxyError.message}`);
      return originalFetch(url, options);
    }
  }

  // got-scraping disabled — use native fetch directly
  // (Re-enable per-host by wrapping with tryGotScrapingFetch when needed)
  return originalFetch(url, options);
}

/**
 * Patched global fetch with env-proxy support and MITM DNS bypass
 */
async function patchedFetch(url, options = {}) {
  return proxyAwareFetch(url, options, null);
}

// Idempotency guard — only patch once to avoid wrapping multiple times
if (globalThis.fetch !== patchedFetch) {

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Verify proxy health and credentials; retry the request once the proxy is reachable
  2. Set strictProxy=true if direct egress is not allowed in your environment (forces a visible error instead of silent bypass)
  3. Check whether the specific upstream blocks the proxy's egress IP and add a different proxy
  4. Ensure the proxy URL scheme matches its type (http:// vs socks5://)
  5. Accept the fallback if direct access is fine — this warning is non-fatal

Example fix

// before
proxyUrl: "http://user:oldpass@proxy:8080"
// after: corrected credentials
proxyUrl: "http://user:newpass@proxy:8080"
Defensive patterns

Strategy: try-catch

Validate before calling

const u = new URL(proxyUrl);
const probe = await fetch('http://' + u.host + '/', { signal: AbortSignal.timeout(3000) }).catch(() => null);
if (!probe) throw new Error(`proxy ${u.host} unreachable`);

Type guard

const isProxyDown = (e) => e instanceof Error && /Proxy required but failed|fetch failed|ECONNREFUSED|407/i.test(e.message);

Try / catch

try {
  return await proxyAwareFetch(url, { proxyOptions, strictProxy: true });
} catch (e) {
  if (isProxyDown(e)) return directFetchWithRetry(url); // explicit, logged direct fallback
  throw e;
}

Prevention

When it happens

Trigger: The browser-fingerprint (got-scraping) proxied request threw — proxy unreachable, 407 auth failure, upstream TLS handshake failure through the proxy, or connection timeout via the proxy dispatcher.

Common situations: Proxy credentials rotated; proxy allows only certain egress hosts; got-scraping's TLS impersonation rejected by the proxy; proxy connection pool exhausted; transient proxy outage during a burst of requests.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/d9502f1d78152446. Report an issue: GitHub.