github/spec-kit · error · ValueError

Aliases for command {cmd_name!r} must be a list

Error message

Aliases for command {cmd_name!r} must be a list

What it means

For each command, the registrar reads the optional 'aliases' field. None and a missing key are tolerated (treated as no aliases), but any other non-list type — a string like "a, b", a dict, a number — raises ValueError('Aliases for command ... must be a list'). This is a schema check on the commands payload passed to register_commands().

Source

Thrown at src/specify_cli/agents.py:702

            if _integ is not None:
                _sep = _integ.invoke_separator_for_mode(registrar_writes_skills)
        except (ImportError, ValueError, KeyError):
            pass
        _prefix = get_invocation_prefix(agent_name, registrar_writes_skills)

        for cmd_info in commands:
            cmd_name = cmd_info["name"]
            aliases = cmd_info.get("aliases", [])
            cmd_file = cmd_info["file"]
            name_reason = relative_extension_path_violation(cmd_name)
            if name_reason:
                raise ValueError(
                    f"Invalid command name {cmd_name!r}: {name_reason}"
                )
            if aliases is None:
                aliases = []
            if not isinstance(aliases, list):
                raise ValueError(
                    f"Aliases for command {cmd_name!r} must be a list"
                )
            for alias in aliases:
                alias_reason = relative_extension_path_violation(alias)
                if alias_reason:
                    raise ValueError(
                        f"Invalid command alias {alias!r}: {alias_reason}"
                    )

            # Guard against path traversal using the single shared policy in
            # relative_extension_path_violation(), so the runtime guard stays
            # aligned with ExtensionManifest._validate() and the skill/preset
            # readers. Skip a malformed/unsafe ``file`` (non-string, empty,
            # whitespace, absolute/anchored, or ``..`` traversal); the
            # resolve()/relative_to() check below is the final containment
            # backstop.
            if relative_extension_path_violation(cmd_file):
                continue

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Change the aliases value to a list of strings: aliases: ["plan", "p"].
  2. Or remove the aliases key (or set it to null) when no aliases are wanted.
  3. Add a pre-check in generators: assert aliases is None or (isinstance(aliases, list) and all(isinstance(a, str) for a in aliases)).

Example fix

# before
{"name": "plan", "aliases": "plan, p", "file": "plan.md"}
# after
{"name": "plan", "aliases": ["plan", "p"], "file": "plan.md"}
Defensive patterns

Strategy: type-guard

Validate before calling

if any(
    not (c.get("aliases") is None or isinstance(c.get("aliases"), list))
    for c in commands
):
    raise SystemExit("every aliases field must be a list or null")

Type guard

from typing import Any

def has_valid_aliases(cmd: dict[str, Any]) -> bool:
    """True when cmd['aliases'] is absent, None, or a list."""
    aliases = cmd.get("aliases")
    return aliases is None or isinstance(aliases, list)

Try / catch

try:
    registrar.register_commands(...)
except ValueError as exc:
    if "must be a list" in str(exc):
        raise SystemExit(f"fix aliases schema: {exc}") from exc
    raise

Prevention

When it happens

Trigger: Passing aliases: "plan, p" (comma string) instead of ["plan", "p"]; aliases: {"plan": true}; aliases: 3; YAML extension manifests where aliases was written as a scalar and loaded verbatim.

Common situations: Hand-writing command descriptors and using a string shorthand that the API does not support; data coming from JSON/YAML configs where the author assumed string splitting; automated generators emitting null vs [] inconsistently (null is fine, scalars are not).

Related errors


AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14). Data as JSON: /api/errors/70e5c8cffa18646b. Report an issue: GitHub.