{"record":{"id":"60e1c1c98669199a","repo":"santifer/career-ops","slug":"plugin-egress-hostname-resolved-to-no-addresse","errorCode":null,"errorMessage":"plugin egress: ${hostname} resolved to no addresses","messagePattern":"plugin egress: (.+?) resolved to no addresses","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"plugins/_net.mjs","lineNumber":97,"sourceCode":"    if (isBlockedIp(hostname)) {\n      if (allowsLocalhost && isLoopbackLiteral(hostname)) return [hostname];\n      throw new Error(`plugin egress to ${hostname} is blocked (private/loopback/metadata range)`);\n    }\n    return [hostname];\n  }\n\n  if (allowsLocalhost && LOOPBACK_HOSTS.has(hostname.toLowerCase())) {\n    // Local-AI providers (Ollama/LM Studio). Resolve but allow loopback through.\n    return ['127.0.0.1'];\n  }\n\n  let addrs;\n  try {\n    addrs = await dnsLookup(hostname, { all: true });\n  } catch (err) {\n    throw new Error(`plugin egress: cannot resolve ${hostname} — ${err.message}`);\n  }\n  if (!addrs.length) throw new Error(`plugin egress: ${hostname} resolved to no addresses`);\n  for (const { address } of addrs) {\n    if (isBlockedIp(address)) {\n      if (allowsLocalhost && isLoopbackLiteral(address)) continue;\n      throw new Error(`plugin egress: ${hostname} resolves to a blocked address (${address}) — possible SSRF/rebinding`);\n    }\n  }\n  return addrs.map(a => a.address);\n}\n\nfunction isLoopbackLiteral(ip) {\n  if (ip === '::1') return true;\n  if (isIP(ip) === 4) return ip.split('.')[0] === '127';\n  return false;\n}\n","sourceCodeStart":79,"sourceCodeEnd":112,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/plugins/_net.mjs#L79-L112","documentation":"Thrown by `resolveAndValidate` (plugins/_net.mjs:97) when `dnsLookup` resolves successfully but returns an empty address array. This is a defensive guard: a well-formed resolver response with zero A/AAAA records is treated as a failure rather than silently producing an empty validated list (which would then connect nowhere or to a default). It is distinct from a DNS error (105) and from a blocked address (107).","triggerScenarios":"The hostname exists in DNS but has no address records (e.g. only MX/TXT records, an apex with no A/AAAA), or a custom resolver returns `{ all: true }` with an empty list. The check `if (!addrs.length)` fires immediately after a successful lookup.","commonSituations":"A domain configured only for email (MX records, no A record) mistakenly used as a fetch host; a misbehaving split-horizon DNS returning empty internally; an IPv6-only hostname queried on an IPv4-only resolver; a temporary resolver glitch returning a valid-but-empty response.","solutions":["Confirm the hostname actually has A/AAAA records: `dig <hostname> A` and `dig <hostname> AAAA`.","If the host is record-less by mistake, add an A record at the DNS provider, or use a hostname that has address records.","If this is a fluke resolver response, retry; if it persists, switch resolver (`/etc/resolv.conf`) or check split-horizon config."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import { lookup } from 'node:dns/promises';\nimport { Resolver } from 'node:dns';\n// Confirm a hostname has A/AAAA records before relying on it.\nasync function assertHasAddressRecords(hostname) {\n  const addrs = await new Resolver().resolve4(hostname).catch(() => [])\n    .concat(await new Resolver().resolve6(hostname).catch(() => []));\n  if (!addrs.length) throw new Error(`'${hostname}' has no A/AAAA records — cannot be a fetch host.`);\n}\nawait assertHasAddressRecords(hostname);","typeGuard":null,"tryCatchPattern":"try {\n  await resolveAndValidate(hostname);\n} catch (err) {\n  if (/resolved to no addresses/.test(err.message)) {\n    console.error(`Config error: ${err.message} — use a host with address records.`);\n  } else throw err;\n}","preventionTips":["Don't use mail-only or record-less hostnames as fetch targets.","Validate A/AAAA existence in config linting."],"tags":["network","dns","egress","plugin"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}