github/spec-kit · error · ValidationError
Aliases for command '{cmd['name']}' must be strings
Error message
Aliases for command '{cmd['name']}' must be strings What it means
One element inside a command's 'aliases' list is not a string (int, dict, null, etc.). After the list-shape check passes, the loader iterates each alias and enforces str type before running the shared path-safety policy on it. Free-form naming is allowed, but type safety is not.
Source
Thrown at src/specify_cli/extensions/__init__.py:503
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():
for entry in coerce_hook_entries(hook_data):
if not isinstance(entry, dict):
raise ValidationError(
f"Hook '{hook_name}' must be a mapping or list of mappings, "
f"got {type(entry).__name__}"View on GitHub (pinned to bf88c9f9a8)
Solutions
- Quote every alias in the manifest so it parses as a string: "aliases": ["build", "42"].
- Remove null/entry elements from the aliases list.
- If generating manifests programmatically, assert all(isinstance(a, str) for a in aliases) before writing.
Example fix
// before "aliases": ["build", 42] // after "aliases": ["build", "42"]
Defensive patterns
Strategy: type-guard
Validate before calling
for c in manifest.get("provides", {}).get("commands", []):
for a in (c.get("aliases") or []):
if not isinstance(a, str):
raise SystemExit(f"non-string alias {a!r} for {c.get('name')}") Type guard
def all_aliases_strings(cmd: dict) -> bool:
return all(isinstance(a, str) for a in (cmd.get("aliases") or [])) Try / catch
try:
ExtensionManifest.load(path)
except ValidationError as e:
if "must be strings" in str(e):
# quote the offending alias element and retry
... Prevention
- Quote every alias in YAML/JSON.
- Filter None out of generated alias lists.
- Assert all(isinstance(a, str), aliases) in manifest generators.
When it happens
Trigger: "aliases": ["build", 42] or "aliases": [null]; YAML unquoted numeric alias like - 42. Raised inside the per-alias loop in ExtensionManifest._validate().
Common situations: Mixing data from config templating where a variable interpolates as a number; YAML parsing an unquoted alias into an int/bool; null sneaking in from optional fields of a generator script.
Related errors
- Invalid command name: expected a string, got {type(cmd['name
- Aliases for command '{cmd['name']}' must be a list
- Invalid {singular} name: expected a string, got {type(name).
- Invalid {singular} description for '{name}': expected a stri
- Invalid runtimes for script '{name}': expected a list of str
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/0885897e40849dc0.
Report an issue: GitHub.