github/spec-kit · error · ValidationError
Aliases for command '{cmd['name']}' must be a list
Error message
Aliases for command '{cmd['name']}' must be a list What it means
A provides.commands entry declared 'aliases' with a value that is not a YAML/JSON list (e.g. a bare string or mapping). Aliases are optional (missing/null normalizes to []), but when present they must be a list; the manifest loader raises ValidationError before any alias content is examined. Note aliases are intentionally free-form strings (no speckit.* pattern enforced) to keep community extensions working.
Source
Thrown at src/specify_cli/extensions/__init__.py:498
f"The extension author should update the manifest to use this name."
)
rename_map[cmd["name"]] = corrected
cmd["name"] = corrected
else:
raise ValidationError(
f"Invalid command name '{cmd['name']}': "
"must follow pattern 'speckit.{extension}.{command}'"
)
# Validate alias types; no pattern enforcement on aliases — they are
# intentionally free-form to preserve community extension compatibility
# (e.g. 'speckit.verify' short aliases used by existing extensions).
aliases = cmd.get("aliases")
if aliases is None:
cmd["aliases"] = []
aliases = []
if not isinstance(aliases, list):
raise ValidationError(
f"Aliases for command '{cmd['name']}' must be a list"
)
for alias in aliases:
if not isinstance(alias, str):
raise ValidationError(
f"Aliases for command '{cmd['name']}' must be strings"
)
alias_reason = relative_extension_path_violation(alias)
if alias_reason:
raise ValidationError(
f"Invalid alias {alias!r} for command "
f"'{cmd['name']}': {alias_reason}"
)
# Rewrite any hook command references that pointed at a renamed command or
# an alias-form ref (ext.cmd → speckit.ext.cmd). Always emit a warning when
# the reference is changed so extension authors know to update the manifest.
for hook_name, hook_data in self.data.get("hooks", {}).items():View on GitHub (pinned to bf88c9f9a8)
Solutions
- Wrap the alias(es) in a list: "aliases": ["build"].
- If no aliases are wanted, remove the key entirely (it defaults to []).
- Ensure YAML/JSON serialization of generated manifests always emits arrays for aliases.
Example fix
// before "aliases": "build" // after "aliases": ["build"]
Defensive patterns
Strategy: validation
Validate before calling
for c in manifest.get("provides", {}).get("commands", []):
if "aliases" in c and c["aliases"] is not None and not isinstance(c["aliases"], list):
raise SystemExit(f"aliases for {c.get('name')} must be a list") Type guard
def has_list_aliases(cmd: dict) -> bool:
a = cmd.get("aliases")
return a is None or isinstance(a, list) Try / catch
try:
ExtensionManifest.load(path)
except ValidationError as e:
if "must be a list" in str(e):
# wrap the aliases value in [ ... ] and retry
... Prevention
- Always emit aliases as JSON arrays even for a single value.
- Omit the aliases key entirely when unused.
- Lint generated manifests for list-typed fields.
When it happens
Trigger: "aliases": "build" (bare string), "aliases": {"short": "build"} (mapping), or "aliases": 3. Occurs while iterating commands during ExtensionManifest._validate().
Common situations: Author used a single string because there is only one alias; YAML folding a one-element flow into a scalar; copy-pasting from a docs example that shows the alias name instead of a list.
Related errors
- Aliases for command '{cmd['name']}' must be strings
- Manifest must be a YAML mapping, got {type(data).__name__}:
- Missing required field: {field}
- Invalid extension: expected a mapping, got {type(ext).__name
- Missing extension.{field}
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/da7f992d4cf594df.
Report an issue: GitHub.