different-ai/openwork · error · LocalManagedMcpPrivateUrlError
the hostname does not resolve
Error message
the hostname does not resolve
What it means
LocalManagedMcpPrivateUrlError thrown by validateResolvedAddresses when DNS lookup of the MCP hostname returns zero addresses. The guard resolves the hostname (via lookup with LookupAllOptions) before allowing the URL; an unresolvable hostname means the server cannot be reached and may also indicate a typo or a dangling internal DNS name.
Source
Thrown at apps/server/src/local-managed-mcp-url-guard.ts:127
throw new LocalManagedMcpPrivateUrlError(rawUrl, `protocol "${url.protocol}" is not allowed`);
}
if (url.username || url.password) {
throw new LocalManagedMcpPrivateUrlError(rawUrl, "embedded URL credentials are not allowed");
}
return url;
}
function allowPrivateUrls(): boolean {
return process.env.OPENWORK_DEV_MODE === "1" || process.env.OPENWORK_ALLOW_PRIVATE_MCP_URLS === "1";
}
type ResolveAddresses = (hostname: string, options: LookupAllOptions) => Promise<LookupAddress[]>;
const resolveAddresses: ResolveAddresses = (hostname, options) => lookup(hostname, options);
function validateResolvedAddresses(hostname: string, addresses: LookupAddress[]): void {
if (addresses.length === 0) {
throw new LocalManagedMcpPrivateUrlError(`https://${hostname}/`, "the hostname does not resolve");
}
if (allowPrivateUrls()) return;
for (const { address } of addresses) {
if (isLocalManagedMcpPrivateAddress(address)) {
throw new LocalManagedMcpPrivateUrlError(
`https://${hostname}/`,
`the hostname resolves to a private or reserved address (${address})`,
);
}
}
}
/**
* Resolves and validates the address inside the socket connector's lookup
* callback. The exact validated answer is handed to net.connect, so a later
* DNS answer cannot replace it between validation and connection.
*/
export function createLocalManagedMcpPublicLookup(View on GitHub (pinned to 2b7df46e8a)
Solutions
- Fix the hostname spelling in the MCP server config
- Test resolution on the same host: `nslookup <hostname>` or `dig <hostname>` — fix /etc/resolv.conf or DNS records if failing
- If it's an internal-only name, ensure the app runs on a network/DNS that can resolve it
- Use an IP or a publicly resolvable hostname if internal DNS cannot be fixed
- Verify the MCP server host is still up and its DNS record exists
Example fix
// before
{ "url": "https://mcp.internal.corp/sse" } // dig: NXDOMAIN
// after
{ "url": "https://mcp.example.com/sse" } // publicly resolvable Defensive patterns
Strategy: validation
Validate before calling
import { lookup } from "node:dns/promises";
async function resolves(hostname: string): Promise<boolean> {
try { return (await lookup(hostname, { all: true })).length > 0; } catch { return false; }
}
// await resolves(new URL(cfg.url).hostname) before registering Try / catch
try {
await mcp.addServer({ url });
} catch (e) {
if (e instanceof LocalManagedMcpPrivateUrlError && e.message.includes("does not resolve")) {
// check hostname spelling / DNS / network, then retry
} else throw e;
} Prevention
- Run `dig`/`nslookup` on the target host before configuring it
- Avoid machine-local hostnames (.local, corp-only names) unless the app's resolver sees them
- Pin to public DNS-resolvable hostnames for shared configs
When it happens
Trigger: assertLocalManagedMcpUrl (or the public lookup) calls validateResolvedAddresses with an empty array from lookup(hostname) — NXDOMAIN/servfail, or the host only has records the resolver can't see (e.g. internal-only DNS).
Common situations: Hostname typo in config; referencing a machine-local name (mybox.local, internal corp DNS) from a resolver that can't see it; DNS outage; the service was decommissioned.
Related errors
- attachment_fetch_failed
- Attachment hostname ${hostname} did not resolve.
- protocol "${url.protocol}" is not allowed
- the hostname resolves to a private or reserved address (${ad
- mcp_server_not_found
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/dcf3cfc81edc1900.
Report an issue: GitHub.