{"record":{"id":"0857d4bf2a6fad69","repo":"diegosouzapw/OmniRoute","slug":"subscription-host-resolution-failed-e-instanceo","errorCode":null,"errorMessage":"Subscription host resolution failed: ${e instanceof Error ? e.message : e}","messagePattern":"Subscription host resolution failed: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/lib/proxySubscription/subscriptionService.ts","lineNumber":316,"sourceCode":"  if (!isSubscriptionFetchUrlAllowed(url, guardOpts)) {\n    throw new Error(\"Subscription URL is not allowed (scheme or host blocked)\");\n  }\n  const host = new URL(url).hostname.toLowerCase();\n  const bare = host.startsWith(\"[\") && host.endsWith(\"]\") ? host.slice(1, -1) : host;\n  if (!isIpLiteral(bare)) {\n    // Hostname: resolve ALL records and refuse if ANY address is internal\n    // (fail closed). A hostname can resolve to multiple records; checking only\n    // the first would let an internal IP slip through if a public record also\n    // exists. `lookup(..., { all: true })` returns every A/AAAA record.\n    try {\n      const dns = await import(\"node:dns\");\n      const addrs = await dns.promises.lookup(bare, { all: true });\n      if (isAnyResolvedAddressBlocked(addrs, guardOpts)) {\n        throw new Error(\"Subscription host resolves to a blocked (internal) address\");\n      }\n    } catch (e) {\n      if (e instanceof Error && e.message.includes(\"blocked\")) throw e;\n      throw new Error(`Subscription host resolution failed: ${e instanceof Error ? e.message : e}`);\n    }\n  }\n}\n\n/**\n * Single fetch attempt: SSRF-validate the target, fetch with manual redirect\n * handling, and re-validate any redirect target. Throws on non-2xx or a\n * blocked target. Each attempt owns its own timeout so a retry after a fast\n * failure isn't killed by the previous attempt's timer.\n */\nasync function doSafeFetch(url: string, headers: Record<string, string>): Promise<string> {\n  await assertSafeFetchTarget(url);\n  const controller = new AbortController();\n  const timeout = setTimeout(() => controller.abort(), SUBSCRIPTION_FETCH_TIMEOUT_MS);\n  try {\n    const res = await fetch(url, { redirect: \"manual\", signal: controller.signal, headers });\n    if (res.status >= 300 && res.status < 400) {\n      const loc = res.headers.get(\"location\");","sourceCodeStart":298,"sourceCodeEnd":334,"githubUrl":"https://github.com/diegosouzapw/OmniRoute/blob/a179ffed5bb2e0b883b9ae7214ce8717b2a94c4d/src/lib/proxySubscription/subscriptionService.ts#L298-L334","documentation":"Error \"Subscription host resolution failed: ${e instanceof Error ? e.message : e}\" thrown in diegosouzapw/OmniRoute.","triggerScenarios":"Thrown at src/lib/proxySubscription/subscriptionService.ts:316 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":[],"exampleFix":null,"handlingStrategy":null,"validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"a179ffed5bb2e0b883b9ae7214ce8717b2a94c4d","analyzedAt":"2026-08-25T18:35:09.898Z","schemaVersion":2},"datasetVersion":"2026-08-25T21:54:21.419Z"}