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

  1. Fix the hostname spelling in the MCP server config
  2. Test resolution on the same host: `nslookup <hostname>` or `dig <hostname>` — fix /etc/resolv.conf or DNS records if failing
  3. If it's an internal-only name, ensure the app runs on a network/DNS that can resolve it
  4. Use an IP or a publicly resolvable hostname if internal DNS cannot be fixed
  5. 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

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


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/dcf3cfc81edc1900. Report an issue: GitHub.