{"record":{"id":"f787443166c3d6b1","repo":"FlowiseAI/Flowise","slug":"dns-resolution-failed-for-hostname","errorCode":null,"errorMessage":"DNS resolution failed for ${hostname}","messagePattern":"DNS resolution failed for (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/components/src/httpSecurity.ts","lineNumber":318,"sourceCode":"    // Strip IPv6 brackets if present\n    if (hostname.startsWith('[') && hostname.endsWith(']')) {\n        hostname = hostname.slice(1, -1)\n    }\n    const protocol: 'http' | 'https' = u.protocol === 'https:' ? 'https' : 'http'\n\n    if (ipaddr.isValid(hostname)) {\n        isDeniedIP(hostname, denyList)\n        return {\n            hostname,\n            ip: hostname,\n            family: hostname.includes(':') ? 6 : 4,\n            protocol\n        }\n    }\n\n    const records = await dns.lookup(hostname, { all: true })\n    if (records.length === 0) {\n        throw new Error(`DNS resolution failed for ${hostname}`)\n    }\n\n    for (const r of records) {\n        isDeniedIP(r.address, denyList)\n    }\n\n    const chosen = records.find((r) => r.family === 4) ?? records[0]\n\n    return {\n        hostname,\n        ip: chosen.address,\n        family: chosen.family as 4 | 6,\n        protocol\n    }\n}\n\nfunction createPinnedAgent(target: ResolvedTarget, options?: { ca?: string | string[] | Buffer }): http.Agent | https.Agent {\n    const Agent = target.protocol === 'https' ? https.Agent : http.Agent","sourceCodeStart":300,"sourceCodeEnd":336,"githubUrl":"https://github.com/FlowiseAI/Flowise/blob/abe4a8601a058047b350c260676826e21dd14101/packages/components/src/httpSecurity.ts#L300-L336","documentation":"Thrown by resolveAndValidate() when dns.lookup() returns an empty records array for the hostname. Normally dns.lookup throws ENOTFOUND for unresolvable hostnames, so reaching this branch (records.length === 0) is itself unusual — it indicates the OS resolver returned no A/AAAA records without throwing. The error surfaces a hostname that cannot be mapped to any IP for deny-list validation.","triggerScenarios":"A URL whose hostname passes the ipaddr.isValid() check (i.e. it is a hostname, not a literal IP) but for which dns.lookup returns zero records. Possible with exotic resolver configurations, transient DNS issues, or hostnames that exist in /etc/hosts with no resolved address. The check at line 317 fires after the await.","commonSituations":"Misconfigured DNS or a hostname typo. A hostname that resolves intermittently. A private DNS zone unreachable from the runtime. /etc/hosts entry with no address. Network partition during resolution. A hostname that only has records of a family not queried.","solutions":["Verify the hostname resolves from the runtime environment: run dig or nslookup in the same container/host.","Retry the request after confirming DNS is healthy (transient failures do occur).","If the hostname is internal, ensure the runtime can reach the corporate/internal DNS resolver.","Fall back to a cached IP if the hostname is known-stable and the failure is transient."],"exampleFix":"// before\nconst resp = await secureFetch('https://flakey-host.example.com/path')\n\n// after\n// pre-check resolution and retry once on failure\nasync function safeFetch(url) {\n  try { return await secureFetch(url) }\n  catch (e) {\n    if (String(e).includes('DNS resolution failed')) {\n      await new Promise(r => setTimeout(r, 500))\n      return secureFetch(url)\n    }\n    throw e\n  }\n}","handlingStrategy":"retry","validationCode":"// Pre-resolve to fail fast with a clear message\nimport dns from 'dns/promises'\n\nasync function ensureResolvable(hostname: string) {\n  const records = await dns.lookup(hostname, { all: true })\n  if (!records.length) throw new Error(`No DNS records for ${hostname}`)\n  return records\n}\n\nawait ensureResolvable(new URL(url).hostname)\nawait secureFetch(url)","typeGuard":null,"tryCatchPattern":"async function fetchWithDnsRetry(url: string, init?: any, retries = 2) {\n  for (let attempt = 0; attempt <= retries; attempt++) {\n    try {\n      return await secureFetch(url, init)\n    } catch (e) {\n      if (String(e).includes('DNS resolution failed') && attempt < retries) {\n        await new Promise((r) => setTimeout(r, 500 * (attempt + 1)))\n        continue\n      }\n      throw e\n    }\n  }\n  throw new Error('unreachable')\n}","preventionTips":["Verify DNS works from the runtime container (dig/nslookup).","Use a stable internal DNS resolver and configure retries at the resolver level.","Cache successful resolutions for short TTLs to ride through transient failures."],"tags":["network","dns","http","ssrf","security"],"backgroundTag":null,"analyzedSha":"abe4a8601a058047b350c260676826e21dd14101","analyzedAt":"2026-08-12T16:04:40.823Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}