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
- Make every alias a flat, relative name with no path separators or traversal (e.g. 'p', 'spec-plan').
- Keep file locations in the 'file' field only — aliases are pure invocation names.
- 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
- Aliases are invocation names, not file paths — keep them flat and separator-free.
- Apply the same naming lint to aliases as to command names.
- Audit extension manifests for aliases containing '/', '\', or '..'.
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
- Output path {candidate!r} escapes directory {base!r}
- Invalid command name {cmd_name!r}: {name_reason}
- Invalid Copilot prompt name {cmd_name!r}: {name_reason}
- Invalid command 'file' {label}: {reason}
- Invalid alias {alias!r} for command '{cmd['name']}': {alias_
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/592088c0c906dcd5.
Report an issue: GitHub.