santifer/career-ops · error · Error
plugin egress: resolved to no addresses
Error message
plugin egress: ${hostname} resolved to no addresses What it means
The plugin egress guard resolves the requested hostname via DNS (dnsLookup with all:true) before any socket is opened, to enforce an SSRF blocklist on the resolved IPs. This error is thrown when the resolver succeeds but returns an empty address list, meaning the name is technically resolvable per the resolver but yields no usable addresses. It indicates the hostname cannot be connected to, so the request is refused before dialing.
Solutions
- Verify the hostname is correct and actually has A/AAAA records: run `node -e "require('dns').promises.lookup('<host>',{all:true}).then(console.log)"` or `dig <host> A +short`.
- Fix the plugin/config hostname value — typos or leftover placeholder hosts are the most common cause.
- Check /etc/hosts and the container's DNS setup for entries that map the name to an empty value.
- If DNS is intentionally empty but you connect by IP anyway, pass a literal IP instead of a hostname (literal IPs skip resolution).
Example fix
// before
await pluginFetch('https://api.internal.example/v1/runs');
// after (host has no DNS records; use the configured literal IP)
await pluginFetch('https://10.20.0.5/v1/runs'); Defensive patterns
Strategy: validation
Validate before calling
const dns = require('dns').promises;
async function hostnameResolvable(host) {
if (/^\d{1,3}(\.\d{1,3}){3}$/.test(host)) return true; // literal IP skips DNS
const addrs = await dns.lookup(host, { all: true });
return Array.isArray(addrs) && addrs.length > 0;
} Type guard
function isResolvableHost(host) {
return typeof host === 'string' && host.length > 0 &&
!/\s/.test(host);
} Try / catch
try {
const res = await pluginFetch(url);
} catch (err) {
if (String(err.message).includes('resolved to no addresses')) {
// fall back to alternate host or surface a config error
return alternateFetch(url);
}
throw err;
} Prevention
- Validate configured hostnames resolve at startup, not at request time.
- Prefer literal IPs or well-known public endpoints over internal-only DNS names.
- Check /etc/hosts and container DNS for empty/placeholder entries.
When it happens
Trigger: Calling any plugin network helper (e.g. fetchJson via the egress wrapper in plugins/_net.mjs) with a hostname whose DNS lookup returns an empty array — e.g. a name with only exotic/unsupported record types, an empty hosts-file entry, or a resolver returning zero A/AAAA records.
Common situations: Typo'd or placeholder hostnames in plugin config (e.g. 'api.example.invalid' configured with an empty record); corporate DNS or /etc/hosts entries mapping a name to nothing; split-horizon DNS returning no addresses from inside a container; IPv6-only environments where a lookup mode yields no records.
Related errors
- plugin egress: resolves to a blocked address ( ) — possible…
- Access denied: Egress guard blocked private target IP
- Blocked request to restricted destination (DNS)
- plugin egress: cannot resolve
- Access denied: Egress guard blocked private target IP
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/60e1c1c98669199a.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/_net.mjs:97
if (isBlockedIp(hostname)) {
if (allowsLocalhost && isLoopbackLiteral(hostname)) return [hostname];
throw new Error(`plugin egress to ${hostname} is blocked (private/loopback/metadata range)`);
}
return [hostname];
}
if (allowsLocalhost && LOOPBACK_HOSTS.has(hostname.toLowerCase())) {
// Local-AI providers (Ollama/LM Studio). Resolve but allow loopback through.
return ['127.0.0.1'];
}
let addrs;
try {
addrs = await dnsLookup(hostname, { all: true });
} catch (err) {
throw new Error(`plugin egress: cannot resolve ${hostname} — ${err.message}`);
}
if (!addrs.length) throw new Error(`plugin egress: ${hostname} resolved to no addresses`);
for (const { address } of addrs) {
if (isBlockedIp(address)) {
if (allowsLocalhost && isLoopbackLiteral(address)) continue;
throw new Error(`plugin egress: ${hostname} resolves to a blocked address (${address}) — possible SSRF/rebinding`);
}
}
return addrs.map(a => a.address);
}
function isLoopbackLiteral(ip) {
if (ip === '::1') return true;
if (isIP(ip) === 4) return ip.split('.')[0] === '127';
return false;
}
View on GitHub (pinned to aac998c7ed)