{"record":{"id":"d9502f1d78152446","repo":"decolua/9router","slug":"proxyfetch-proxy-failed-falling-back-to-direct-d9502f","errorCode":null,"errorMessage":"[ProxyFetch] Proxy failed, falling back to direct: ${proxyError.message}","messagePattern":"\\[ProxyFetch\\] Proxy failed, falling back to direct: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"open-sse/utils/proxyFetch.js","lineNumber":346,"sourceCode":"    try {\n      const parsedUrl = new URL(targetUrl);\n      const realIP = await resolveRealIP(parsedUrl.hostname);\n      if (realIP) return await createBypassRequest(parsedUrl, realIP, options);\n    } catch (error) {\n      console.warn(`[ProxyFetch] MITM bypass failed: ${error.message}`);\n    }\n  }\n\n  if (proxyUrl) {\n    try {\n      const dispatcher = await getDispatcher(proxyUrl);\n      return await originalFetch(url, { ...options, dispatcher });\n    } catch (proxyError) {\n      // If strictProxy is enabled, fail hard instead of falling back to direct\n      if (proxyOptions?.strictProxy === true) {\n        throw new Error(`[ProxyFetch] Proxy required but failed (strictProxy=true): ${proxyError.message}`);\n      }\n      console.warn(`[ProxyFetch] Proxy failed, falling back to direct: ${proxyError.message}`);\n      return originalFetch(url, options);\n    }\n  }\n\n  // got-scraping disabled — use native fetch directly\n  // (Re-enable per-host by wrapping with tryGotScrapingFetch when needed)\n  return originalFetch(url, options);\n}\n\n/**\n * Patched global fetch with env-proxy support and MITM DNS bypass\n */\nasync function patchedFetch(url, options = {}) {\n  return proxyAwareFetch(url, options, null);\n}\n\n// Idempotency guard — only patch once to avoid wrapping multiple times\nif (globalThis.fetch !== patchedFetch) {","sourceCodeStart":328,"sourceCodeEnd":364,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/utils/proxyFetch.js#L328-L364","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify proxy health and credentials; retry the request once the proxy is reachable","Set strictProxy=true if direct egress is not allowed in your environment (forces a visible error instead of silent bypass)","Check whether the specific upstream blocks the proxy's egress IP and add a different proxy","Ensure the proxy URL scheme matches its type (http:// vs socks5://)","Accept the fallback if direct access is fine — this warning is non-fatal"],"exampleFix":"// before\nproxyUrl: \"http://user:oldpass@proxy:8080\"\n// after: corrected credentials\nproxyUrl: \"http://user:newpass@proxy:8080\"","handlingStrategy":"try-catch","validationCode":"const u = new URL(proxyUrl);\nconst probe = await fetch('http://' + u.host + '/', { signal: AbortSignal.timeout(3000) }).catch(() => null);\nif (!probe) throw new Error(`proxy ${u.host} unreachable`);","typeGuard":"const isProxyDown = (e) => e instanceof Error && /Proxy required but failed|fetch failed|ECONNREFUSED|407/i.test(e.message);","tryCatchPattern":"try {\n  return await proxyAwareFetch(url, { proxyOptions, strictProxy: true });\n} catch (e) {\n  if (isProxyDown(e)) return directFetchWithRetry(url); // explicit, logged direct fallback\n  throw e;\n}","preventionTips":["Test got-scraping + proxy path once at boot; alert if the probe fails","Rotate proxy credentials before expiry to avoid 407s","Use a proxy that permits got-scraping's TLS impersonation","Set strictProxy=true to convert silent bypass into actionable errors"],"tags":["proxy","got-scraping","fallback","network"],"backgroundTag":"proxy-connection-failed","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}