musistudio/claude-code-router · error · Error

cannot target a local or internal host.

Error message

${label} cannot target a local or internal host.

What it means

validateRemoteHostname blocks hostnames that resolve to local/internal scopes: 'localhost', '*.localhost', or suffixes .home, .lan, .local, .internal. Remote manifests must only target publicly reachable hosts — this is the first half of an SSRF guard (hostname scope), complemented by IP resolution checks in resolveSafeAddress.

Solutions

  1. Point the manifest at the public DNS name of the service
  2. Expose the internal service through a public HTTPS endpoint (reverse proxy) if it must be referenced remotely
  3. Use a local manifest file for internal/lab targets instead of a remote manifest

Example fix

// before
"baseUrl": "https://inference.internal:8080"
// after
"baseUrl": "https://inference.example.com"
Defensive patterns

Strategy: validation

Validate before calling

const BANNED = [/^localhost$/i, /\.localhost$/i, /\.(home|lan|local|internal)$/i];
if (BANNED.some(r => r.test(hostname))) throw new Error('internal host not allowed');

Prevention

When it happens

Trigger: A manifest URL with hostname 'localhost', 'mybox.lan', 'printer.local', 'service.internal', or anything ending in '.localhost'.

Common situations: Developer points a 'remote' manifest at an internal service for testing; homelab DNS suffixes (.lan/.home) reused in production manifests; mDNS-style .local names in docs copied into config.

Related errors


AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27). Data as JSON: /api/errors/9326381b6a26f983. Report an issue: GitHub.

Appendix: source

Thrown at packages/core/src/providers/manifest-service.ts:235

  }
  validateRemoteHostname(url.hostname, label);
  await resolveSafeAddress(url.hostname);
}

function validateRemoteHostname(hostname: string, label: string): void {
  const normalized = hostname.trim().toLowerCase().replace(/\.$/, "");
  if (!normalized) {
    throw new Error(`${label} is invalid.`);
  }
  if (
    normalized === "localhost" ||
    normalized.endsWith(".localhost") ||
    normalized.endsWith(".home") ||
    normalized.endsWith(".lan") ||
    normalized.endsWith(".local") ||
    normalized.endsWith(".internal")
  ) {
    throw new Error(`${label} cannot target a local or internal host.`);
  }
}

async function resolveSafeAddress(hostname: string): Promise<SafeAddress> {
  const addresses = await lookup(hostname, { all: true, verbatim: true });
  if (addresses.length === 0) {
    throw new Error(`Could not resolve host: ${hostname}`);
  }

  for (const address of addresses) {
    if (!isPublicIpAddress(address.address)) {
      throw new Error(`Remote manifest host resolved to a private or reserved address: ${address.address}`);
    }
  }

  const first = addresses[0];
  return {
    address: first.address,

View on GitHub (pinned to 99f24806c6)