garrytan/gstack · error

Unknown host '${name}'. Valid hosts: ${ALL_HOST_NAMES.join('

Error message

Unknown host '${name}'. Valid hosts: ${ALL_HOST_NAMES.join(', ')}

What it means

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.

Source

Thrown at hosts/index.ts:38

/** All registered host configs. Add new hosts here. */
export const ALL_HOST_CONFIGS: HostConfig[] = [claude, codex, factory, kiro, opencode, slate, cursor, openclaw, hermes, gbrain];

/** Map from host name to config. */
export const HOST_CONFIG_MAP: Record<string, HostConfig> = Object.fromEntries(
  ALL_HOST_CONFIGS.map(c => [c.name, c])
);

/** Union type of all host names, derived from configs. */
export type Host = (typeof ALL_HOST_CONFIGS)[number]['name'];

/** All host names as a string array (for CLI arg validation, etc.). */
export const ALL_HOST_NAMES: string[] = ALL_HOST_CONFIGS.map(c => c.name);

/** Get a host config by name. Throws if not found. */
export function getHostConfig(name: string): HostConfig {
  const config = HOST_CONFIG_MAP[name];
  if (!config) {
    throw new Error(`Unknown host '${name}'. Valid hosts: ${ALL_HOST_NAMES.join(', ')}`);
  }
  return config;
}

/**
 * Resolve a host name from a CLI argument, handling aliases.
 * e.g., 'agents' → 'codex', 'droid' → 'factory'
 */
export function resolveHostArg(arg: string): string {
  // Direct name match
  if (HOST_CONFIG_MAP[arg]) return arg;

  // Alias match
  for (const config of ALL_HOST_CONFIGS) {
    if (config.cliAliases?.includes(arg)) return config.name;
  }

  throw new Error(`Unknown host '${arg}'. Valid hosts: ${ALL_HOST_NAMES.join(', ')}`);

View on GitHub (pinned to 94993f7401)

Solutions

  1. Use the comma-separated list in the error message to pick the exact canonical name.
  2. For aliases like 'agents' or 'droid', call resolveHostArg() first to canonicalize before getHostConfig().
  3. If adding a new host, import its config and append it to ALL_HOST_CONFIGS in hosts/index.ts.
  4. Lowercase the input if case is the issue — names are stored lowercase.

Example fix

// before — alias passed to getHostConfig throws
const cfg = getHostConfig('agents');

// after — resolve alias to canonical name first
const cfg = getHostConfig(resolveHostArg('agents')); // → codex
Defensive patterns

Strategy: validation

Validate before calling

import { ALL_HOST_NAMES, HOST_CONFIG_MAP } from './hosts';
function assertHostKnown(name: string): void {
  if (!HOST_CONFIG_MAP[name]) {
    throw new Error(`'${name}' is not registered. Known: ${ALL_HOST_NAMES.join(', ')}`);
  }
}

Type guard

import { ALL_HOST_NAMES } from './hosts';
const HOST_NAME_SET = new Set(ALL_HOST_NAMES);
function isHostName(name: string): boolean {
  return typeof name === 'string' && HOST_NAME_SET.has(name);
}

Try / catch

try {
  return getHostConfig(name);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Unknown host')) {
    return getHostConfig(resolveHostArg(name.toLowerCase())); // try alias
  }
  throw e;
}

Prevention

When it happens

Trigger: 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'.

Common situations: 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.

Related errors


AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12). Data as JSON: /api/errors/24b90f753b8bf68e. Report an issue: GitHub.