{"record":{"id":"987d99dc389a57cb","repo":"musistudio/claude-code-router","slug":"could-not-resolve-host-hostname","errorCode":null,"errorMessage":"Could not resolve host: ${hostname}","messagePattern":"Could not resolve host: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/providers/manifest-service.ts","lineNumber":242,"sourceCode":"  if (!normalized) {\n    throw new Error(`${label} is invalid.`);\n  }\n  if (\n    normalized === \"localhost\" ||\n    normalized.endsWith(\".localhost\") ||\n    normalized.endsWith(\".home\") ||\n    normalized.endsWith(\".lan\") ||\n    normalized.endsWith(\".local\") ||\n    normalized.endsWith(\".internal\")\n  ) {\n    throw new Error(`${label} cannot target a local or internal host.`);\n  }\n}\n\nasync function resolveSafeAddress(hostname: string): Promise<SafeAddress> {\n  const addresses = await lookup(hostname, { all: true, verbatim: true });\n  if (addresses.length === 0) {\n    throw new Error(`Could not resolve host: ${hostname}`);\n  }\n\n  for (const address of addresses) {\n    if (!isPublicIpAddress(address.address)) {\n      throw new Error(`Remote manifest host resolved to a private or reserved address: ${address.address}`);\n    }\n  }\n\n  const first = addresses[0];\n  return {\n    address: first.address,\n    family: first.family === 6 ? 6 : 4\n  };\n}\n\nfunction isPublicIpAddress(address: string): boolean {\n  const family = net.isIP(address);\n  if (family === 4) {","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/core/src/providers/manifest-service.ts#L224-L260","documentation":"resolveSafeAddress DNS-resolves the manifest host (dns.lookup with all:true) and throws when the lookup returns zero records. The manifest cannot be used because its host has no addresses to safety-check.","triggerScenarios":"validatePublicHttpsUrl calls resolveSafeAddress(hostname) and dns.lookup returns an empty array — typically NXDOMAIN handled by the resolver shim, or a resolver returning no A/AAAA records.","commonSituations":"Typos in the domain, stale/decommissioned manifest hosts, DNS propagation delays after moving the manifest, or restrictive resolvers returning empty answers.","solutions":["Fix the hostname spelling in the manifest/provider config","Verify DNS: `dig <host>` / `node -e \"require('dns').lookup('host',{all:true},console.log)\"` should return records","Wait for DNS propagation if the domain was just created or migrated","If the domain is truly gone, remove or replace the manifest reference"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"import { lookup } from 'node:dns/promises';\nconst addrs = await lookup(hostname, { all: true });\nif (addrs.length === 0) throw new Error(`no DNS records for ${hostname}`);","typeGuard":null,"tryCatchPattern":"catch (e) { if (e instanceof Error && e.message.startsWith('Could not resolve host')) return cachedManifest ?? null; }","preventionTips":["Verify domains with dig before shipping manifests","Cache last-good manifests for resilience"],"tags":["dns","network","manifest","ssrf"],"backgroundTag":"dns-resolution-failed","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}