{"record":{"id":"0ac87637b2ae85be","repo":"decolua/9router","slug":"proxyfetch-proxy-required-but-failed-strictprox","errorCode":null,"errorMessage":"[ProxyFetch] Proxy required but failed (strictProxy=true): ${proxyError.message}","messagePattern":"\\[ProxyFetch\\] Proxy required but failed \\(strictProxy=true\\): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"open-sse/utils/proxyFetch.js","lineNumber":322,"sourceCode":"      \"x-relay-path\": `${parsed.pathname}${parsed.search}`,\n    };\n    return originalFetch(vercelRelayUrl, { ...options, headers: relayHeaders });\n  }\n\n  const connectionProxyUrl = resolveConnectionProxyUrl(targetUrl, proxyOptions);\n  const envProxyUrl = connectionProxyUrl ? null : normalizeProxyUrl(getEnvProxyUrl(targetUrl));\n  const proxyUrl = connectionProxyUrl || envProxyUrl;\n\n  // MITM DNS bypass: for known MITM-intercepted hosts, resolve real IP to avoid DNS spoof\n  if (shouldBypassMitmDns(targetUrl)) {\n    if (proxyUrl) {\n      // Proxy resolves DNS externally (not affected by /etc/hosts) — use proxy directly\n      try {\n        const dispatcher = await getDispatcher(proxyUrl);\n        return await originalFetch(url, { ...options, dispatcher });\n      } catch (proxyError) {\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 bypass: ${proxyError.message}`);\n      }\n    }\n    // No proxy — manually resolve real IP to bypass DNS spoof\n    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 });","sourceCodeStart":304,"sourceCodeEnd":340,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/utils/proxyFetch.js#L304-L340","documentation":"In proxyAwareFetch, when the target host is MITM-intercepted and a proxy URL is configured, the fetch is attempted through the proxy dispatcher. If that proxy request throws and proxyOptions.strictProxy === true, it throws \"[ProxyFetch] Proxy required but failed (strictProxy=true): <reason>\" instead of silently falling back to a direct (possibly DNS-spoofed) connection. This is the MITM-bypass branch (line ~322).","triggerScenarios":"Target host is in the MITM-bypass list, a proxy URL is resolved (connection-level or env), and the proxied fetch rejects — proxy down, proxy auth rejected, CONNECT tunnel refused — while strictProxy is enabled.","commonSituations":"Corporate proxy credentials rotated or expired; local proxy (Clash/V2Ray) not running on the configured port; proxy URL scheme unsupported by the dispatcher; strictProxy=true left on after disabling the proxy client.","solutions":["Verify the proxy is actually running and reachable at the configured URL (curl -x <proxyUrl> https://target).","If direct connection is safe for this host, set strictProxy to false (or omit it) to allow fallback to direct/bypass.","Check proxy auth credentials and protocol (http vs socks5) in the proxy URL.","Inspect the embedded <reason>: ECONNREFUSED means proxy is down; 407 means auth failed."],"exampleFix":"// before\nawait proxyAwareFetch(url, opts, { proxyUrl, strictProxy: true }); // proxy down\n// after — allow fallback when direct access is acceptable\nawait proxyAwareFetch(url, opts, { proxyUrl, strictProxy: false });","handlingStrategy":"try-catch","validationCode":"// pre-check proxy reachability before enabling strictProxy:\nconst ok = await fetch(\"https://example.com\", { dispatcher: await getDispatcher(proxyUrl) }).then(r => r.ok).catch(() => false);\nif (!ok && requireStrict) throw new Error(\"Proxy unreachable — fix proxy before calling\");\n","typeGuard":"const strictOpts = (o) => ({ ...o, strictProxy: o?.strictProxy === true }); // make strict intent explicit","tryCatchPattern":"try {\n  return await proxyAwareFetch(url, options, { ...proxyOptions, strictProxy: true });\n} catch (e) {\n  if (String(e.message).includes(\"strictProxy=true\")) {\n    // do NOT fall back silently; alert ops / switch proxy endpoint\n  } else throw e;\n}","preventionTips":["Health-check the proxy endpoint at startup and before long jobs.","Only enable strictProxy when direct leakage is unacceptable; otherwise leave it off.","Monitor the embedded cause (ECONNREFUSED, 407, TLS) to distinguish down-proxy vs auth issues.","Keep proxy auth credentials in sync when your proxy provider rotates them."],"tags":["network","proxy","strict-proxy","dns"],"backgroundTag":"proxy-connection-failed","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}