github/spec-kit · error · ValidationError

Invalid alias {alias!r} for command '{cmd['name']}': {alias_

Error message

Invalid alias {alias!r} for command '{cmd['name']}': {alias_reason}

What it means

An alias string failed the same relative_extension_path_violation() policy used for command files (src/specify_cli/_utils.py:21): aliases are free-form but must remain safe relative output paths. Rejected shapes include absolute/anchored paths (leading '/', drive letters, UNC), '..' traversal segments, backslash separators, leading/trailing whitespace, empty values, and trailing directory slashes, plus platform-reserved components.

Source

Thrown at src/specify_cli/extensions/__init__.py:508

            # 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__}"
                    )
                command_ref = entry.get("command")
                if not isinstance(command_ref, str):
                    continue
                # Step 1: apply any rename from the auto-correction pass.

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Make the alias a plain relative slug with no slashes or '..' segments, e.g. "build" or "myext.build".
  2. Strip whitespace and drop any leading '/', drive letters, or backslashes.
  3. Audit third-party extension manifests for traversal-shaped aliases before installing them.

Example fix

// before
"aliases": ["../commands/build"]
// after
"aliases": ["build"]
Defensive patterns

Strategy: validation

Validate before calling

from specify_cli._utils import relative_extension_path_violation

for c in manifest["provides"]["commands"]:
    for a in (c.get("aliases") or []):
        if isinstance(a, str):
            reason = relative_extension_path_violation(a)
            assert reason is None, f"unsafe alias {a!r}: {reason}"

Type guard

def is_safe_alias(alias: str) -> bool:
    from specify_cli._utils import relative_extension_path_violation
    return relative_extension_path_violation(alias) is None

Try / catch

try:
    ExtensionManifest.load(path)
except ValidationError as e:
    if "Invalid alias" in str(e):
        # replace the traversal-shaped alias with a plain slug and retry
        ...

Prevention

When it happens

Trigger: "aliases": ["../evil"], "aliases": ["/etc/passwd"], "aliases": ["C:\\tmp"], or "aliases": ["build/"]. Raised in the per-alias loop right after the string-type check.

Common situations: Attempting path-traversal via an alias in a third-party extension (security gate); mistakenly treating the alias field as a file path; aliases ending in '/' copied from path constants.

Related errors


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