{"record":{"id":"0a468c1609ad1912","repo":"musistudio/claude-code-router","slug":"label-is-invalid","errorCode":null,"errorMessage":"${label} is invalid.","messagePattern":"(.+?) is invalid\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/providers/manifest-service.ts","lineNumber":225,"sourceCode":"  }\n}\n\nasync function validatePublicHttpsUrl(value: string, label: string): Promise<void> {\n  const url = new URL(providerUrlWithDefaultScheme(value));\n  if (url.protocol !== \"https:\") {\n    throw new Error(`${label} from a remote manifest must use https.`);\n  }\n  if (url.username || url.password) {\n    throw new Error(`${label} cannot include credentials.`);\n  }\n  validateRemoteHostname(url.hostname, label);\n  await resolveSafeAddress(url.hostname);\n}\n\nfunction validateRemoteHostname(hostname: string, label: string): void {\n  const normalized = hostname.trim().toLowerCase().replace(/\\.$/, \"\");\n  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  }","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/core/src/providers/manifest-service.ts#L207-L243","documentation":"validateRemoteHostname normalizes the hostname and throws '${label} is invalid.' when nothing remains after trimming, lowercasing, and stripping a trailing dot — i.e. the URL had no usable host.","triggerScenarios":"A URL whose hostname normalizes to empty, e.g. 'https://', 'https://.', or a value like 'https://:8443' that parses but yields no host.","commonSituations":"Template placeholder left unfilled (https://${HOST} with HOST empty), malformed URL construction, or trailing-dot-only hostnames in hand-written manifests.","solutions":["Fix or fill in the host portion of the URL in the manifest/provider config","Add a pre-flight check that new URL(...).hostname is non-empty before shipping config","If the value comes from env substitution, fail fast at startup when the variable is empty"],"exampleFix":"// before\n\"baseUrl\": `https://${process.env.PROVIDER_HOST}` // HOST unset\n// after\nconst host = process.env.PROVIDER_HOST;\nif (!host) throw new Error('PROVIDER_HOST is required');\n\"baseUrl\": `https://${host}`","handlingStrategy":"validation","validationCode":"const host = new URL(providerUrlWithDefaultScheme(url)).hostname.trim().toLowerCase().replace(/\\.$/,'');\nif (!host) throw new Error('hostname is empty');","typeGuard":"function hasHostname(v: string): boolean { try { return new URL(v).hostname.length > 0; } catch { return false; } }","tryCatchPattern":null,"preventionTips":["Fail fast when env-provided hosts are empty","Lint manifests for empty host placeholders"],"tags":["url-validation","hostname","manifest"],"backgroundTag":"invalid-hostname","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}