{"record":{"id":"24b90f753b8bf68e","repo":"garrytan/gstack","slug":"unknown-host-name-valid-hosts-all-host-na","errorCode":null,"errorMessage":"Unknown host '${name}'. Valid hosts: ${ALL_HOST_NAMES.join(', ')}","messagePattern":"Unknown host '(.+?)'\\. Valid hosts: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hosts/index.ts","lineNumber":38,"sourceCode":"/** All registered host configs. Add new hosts here. */\nexport const ALL_HOST_CONFIGS: HostConfig[] = [claude, codex, factory, kiro, opencode, slate, cursor, openclaw, hermes, gbrain];\n\n/** Map from host name to config. */\nexport const HOST_CONFIG_MAP: Record<string, HostConfig> = Object.fromEntries(\n  ALL_HOST_CONFIGS.map(c => [c.name, c])\n);\n\n/** Union type of all host names, derived from configs. */\nexport type Host = (typeof ALL_HOST_CONFIGS)[number]['name'];\n\n/** All host names as a string array (for CLI arg validation, etc.). */\nexport const ALL_HOST_NAMES: string[] = ALL_HOST_CONFIGS.map(c => c.name);\n\n/** Get a host config by name. Throws if not found. */\nexport function getHostConfig(name: string): HostConfig {\n  const config = HOST_CONFIG_MAP[name];\n  if (!config) {\n    throw new Error(`Unknown host '${name}'. Valid hosts: ${ALL_HOST_NAMES.join(', ')}`);\n  }\n  return config;\n}\n\n/**\n * Resolve a host name from a CLI argument, handling aliases.\n * e.g., 'agents' → 'codex', 'droid' → 'factory'\n */\nexport function resolveHostArg(arg: string): string {\n  // Direct name match\n  if (HOST_CONFIG_MAP[arg]) return arg;\n\n  // Alias match\n  for (const config of ALL_HOST_CONFIGS) {\n    if (config.cliAliases?.includes(arg)) return config.name;\n  }\n\n  throw new Error(`Unknown host '${arg}'. Valid hosts: ${ALL_HOST_NAMES.join(', ')}`);","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/hosts/index.ts#L20-L56","documentation":"Thrown by getHostConfig() when the requested host name has no entry in the HOST_CONFIG_MAP registry built from ALL_HOST_CONFIGS. It is a fail-closed lookup: the function promises a HostConfig and refuses to return undefined, listing every registered host name in the message so the caller can self-correct. Adding a host requires both a config file and registration in hosts/index.ts.","triggerScenarios":"Call getHostConfig(name) where name is undefined, misspelled, differently-cased, or genuinely not in the registry (e.g. a host whose config file exists but was never imported into ALL_HOST_CONFIGS). Case-sensitive: 'Claude' will not match 'claude'.","commonSituations":"Typo in a CLI flag or config value; a new host was added as a file but the author forgot to append it to ALL_HOST_CONFIGS; passing a host alias (e.g. 'agents') to getHostConfig instead of resolveHostArg; case mismatch.","solutions":["Use the comma-separated list in the error message to pick the exact canonical name.","For aliases like 'agents' or 'droid', call resolveHostArg() first to canonicalize before getHostConfig().","If adding a new host, import its config and append it to ALL_HOST_CONFIGS in hosts/index.ts.","Lowercase the input if case is the issue — names are stored lowercase."],"exampleFix":"// before — alias passed to getHostConfig throws\nconst cfg = getHostConfig('agents');\n\n// after — resolve alias to canonical name first\nconst cfg = getHostConfig(resolveHostArg('agents')); // → codex","handlingStrategy":"validation","validationCode":"import { ALL_HOST_NAMES, HOST_CONFIG_MAP } from './hosts';\nfunction assertHostKnown(name: string): void {\n  if (!HOST_CONFIG_MAP[name]) {\n    throw new Error(`'${name}' is not registered. Known: ${ALL_HOST_NAMES.join(', ')}`);\n  }\n}","typeGuard":"import { ALL_HOST_NAMES } from './hosts';\nconst HOST_NAME_SET = new Set(ALL_HOST_NAMES);\nfunction isHostName(name: string): boolean {\n  return typeof name === 'string' && HOST_NAME_SET.has(name);\n}","tryCatchPattern":"try {\n  return getHostConfig(name);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Unknown host')) {\n    return getHostConfig(resolveHostArg(name.toLowerCase())); // try alias\n  }\n  throw e;\n}","preventionTips":["Always resolve CLI args through resolveHostArg before getHostConfig.","Use the Host union type, not string, for host-name parameters so the compiler rejects unknowns.","When adding a host, update ALL_HOST_CONFIGS and re-export in the same commit.","Add a unit test asserting every alias resolves to a known host."],"tags":["config","registry","host-config","validation"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}