santifer/career-ops · error · Error
plugin egress: cannot resolve
Error message
plugin egress: cannot resolve ${hostname} — ${err.message} What it means
For non-IP-literal hostnames, resolveAndValidate performs a DNS lookup (dnsLookup with all: true) before checking addresses. If resolution itself fails, the original error message is wrapped and rethrown as this error. DNS failure means the egress guard cannot verify the destination, so the request is refused rather than passed through unresolved.
Solutions
- Check network/DNS connectivity (`ping`/`nslookup <hostname>`) and fix the resolver or reconnect before retrying.
- Correct the hostname in the plugin configuration if it is a typo.
- Connect the VPN or network on which the hostname resolves, if it is an internal-only name.
- Retry on transient failures — wrap the call in retry with backoff since DNS outages are often momentary.
- If the name is stable and known, use its public IP literal (which skips DNS) — but note it must not be in a blocked range.
Example fix
// before: single attempt dies on transient DNS failure
const addrs = await resolveAndValidate(hostname);
// after: retry a few times with backoff
let addrs;
for (let i = 0; i < 3; i++) {
try { addrs = await resolveAndValidate(hostname); break; }
catch (e) {
if (!e.message.startsWith("plugin egress: cannot resolve") || i === 2) throw e;
await new Promise(r => setTimeout(r, 500 * 2 ** i));
}
} Defensive patterns
Strategy: retry
Validate before calling
function isProbablyResolvable(hostname) {
return typeof hostname === 'string' && hostname.length > 0 && !hostname.includes(' ') && !isIP(hostname);
} Try / catch
async function resolveWithRetry(hostname, opts, attempts = 3) {
for (let i = 0; i < attempts; i++) {
try {
return await resolveAndValidate(hostname, opts);
} catch (err) {
const isDnsFail = err.message.startsWith('plugin egress: cannot resolve ');
if (!isDnsFail || i === attempts - 1) throw err;
await new Promise(r => setTimeout(r, 500 * 2 ** i));
}
}
} Prevention
- Verify the hostname spelling and DNS record at plugin-install/config time.
- Retry DNS resolution with backoff; outages are often transient.
- Detect offline/VPN-required environments early and surface a clear message.
- Prefer stable public hostnames with reliable DNS over internal-only names.
When it happens
Trigger: Calling resolveAndValidate(hostname) where hostname has no DNS record (NXDOMAIN), the machine has no network/DNS connectivity, DNS times out, /etc/resolv.conf is broken, or the hostname is malformed so the resolver rejects it.
Common situations: Offline laptop or CI runner without network access; typo'd API hostname in plugin config; corporate DNS blocking the domain; hostname only resolvable on a VPN that is not connected; transient DNS server outage.
Related errors
- Access denied: Egress guard blocked private target IP
- plugin egress: resolved to no addresses
- plugin egress: resolves to a blocked address ( ) — possible…
- plugin egress to is blocked (private/loopback/metadata…
- Access denied: Egress guard blocked private target IP
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/467f2fb0ad608e63.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/_net.mjs:95
// An IP literal host: validate directly (no DNS).
if (isIP(hostname)) {
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)