github/spec-kit · error · ValueError

Invalid command alias {alias!r}: {alias_reason}

Error message

Invalid command alias {alias!r}: {alias_reason}

What it means

Each entry in a command's aliases list is checked with the same relative_extension_path_violation() policy as command names: aliases must be safe relative path components (no separators, no '..', not absolute/anchored/empty). A violating alias raises ValueError with the alias and reason before any files are written.

Source

Thrown at src/specify_cli/agents.py:708

        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
            try:
                source_file = (source_root / cmd_file).resolve()
                source_file.relative_to(source_root)  # raises ValueError if outside
            except (OSError, ValueError):
                continue

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Make every alias a flat, relative name with no path separators or traversal (e.g. 'p', 'spec-plan').
  2. Keep file locations in the 'file' field only — aliases are pure invocation names.
  3. Run the shared validator on aliases in your generator before calling register_commands().

Example fix

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

Strategy: validation

Validate before calling

from specify_cli import relative_extension_path_violation

unsafe = [
    a for c in commands for a in (c.get("aliases") or [])
    if relative_extension_path_violation(a)
]
if unsafe:
    raise SystemExit(f"unsafe aliases: {unsafe}")

Type guard

from specify_cli import relative_extension_path_violation

def is_safe_alias(alias: str) -> bool:
    """True when the alias passes the shared path-safety policy."""
    return relative_extension_path_violation(alias) is None

Try / catch

try:
    registrar.register_commands(...)
except ValueError as exc:
    if "Invalid command alias" in str(exc):
        raise SystemExit(f"rejected unsafe alias: {exc}") from exc
    raise

Prevention

When it happens

Trigger: aliases: ["../root", "/abs", "a/b", ""] — an alias intended as an alternate invocation name but containing path syntax; aliases copied from full command file paths rather than short names.

Common situations: Confusing the alias (a short alternate name) with the file path; extension manifests reusing a relative file path as an alias; refactors that moved names into subpaths without updating aliases to flat names.

Related errors


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