github/spec-kit · error · ValidationError

Invalid command name '{cmd['name']}': must follow pattern 's

Error message

Invalid command name '{cmd['name']}': must follow pattern 'speckit.{extension}.{command}'

What it means

A command name did not match the required pattern ^speckit\.([a-z0-9-]+)\.([a-z0-9-]+)$ (EXTENSION_COMMAND_NAME_PATTERN, extensions/__init__.py:62) AND the loader could not auto-correct it via _try_correct_command_name(). The auto-corrector fixes two legacy forms — 'speckit.command' → 'speckit.{ext_id}.command' and '{ext_id}.command' → 'speckit.{ext_id}.command' — so only names outside those salvageable formats reach this hard error. This is a manifest-authoring contract: all extension commands live in the speckit.{extension}.{command} namespace.

Source

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

            cmd_file = cmd["file"]
            reason = relative_extension_path_violation(cmd_file)
            if reason:
                label = repr(cmd_file) if isinstance(cmd_file, str) else f"for command '{cmd.get('name')}'"
                raise ValidationError(f"Invalid command 'file' {label}: {reason}")

            # Validate command name format
            if not EXTENSION_COMMAND_NAME_PATTERN.match(cmd["name"]):
                corrected = self._try_correct_command_name(cmd["name"], ext["id"])
                if corrected:
                    self.warnings.append(
                        f"Command name '{cmd['name']}' does not follow the required pattern "
                        f"'speckit.{{extension}}.{{command}}'. Registering as '{corrected}'. "
                        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(

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Rename the command in the manifest to 'speckit.<your-extension-id>.<lowercase-slug>', e.g. "speckit.myext.build".
  2. Keep every segment lowercase alphanumeric plus hyphens; use hyphens not underscores.
  3. If you intended a short alias like 'speckit.verify', keep the canonical three-part name and declare the short form under 'aliases' instead (aliases are free-form).

Example fix

// before
{ "name": "build", "file": "commands/build.md" }
// after
{ "name": "speckit.myext.build", "file": "commands/build.md", "aliases": ["build"] }
Defensive patterns

Strategy: validation

Validate before calling

import re
PATTERN = re.compile(r"^speckit\.([a-z0-9-]+)\.([a-z0-9-]+)$")

bad = [c["name"] for c in manifest["provides"]["commands"]
       if "name" in c and isinstance(c["name"], str) and not PATTERN.match(c["name"])]
assert not bad, f"non-conforming command names (not auto-correctable?): {bad}"

Type guard

def is_canonical_command_name(name: str, ext_id: str) -> bool:
    import re
    return bool(re.match(r"^speckit\.[a-z0-9-]+\.[a-z0-9-]+$", name)) and name.split(".")[1] == ext_id

Try / catch

try:
    ExtensionManifest.load(path)
except ValidationError as e:
    if "must follow pattern 'speckit" in str(e):
        # rename to speckit.<ext-id>.<slug>; note typos may be auto-corrected with a warning instead
        ...

Prevention

When it happens

Trigger: Declaring a command named "build", "myext:build", "speckit.MyExt.Build" (uppercase), "speckit.myext.sub.build" (three segments), or "speckit.otherext.build" (wrong extension id that correction cannot derive). The pattern check runs after the rename-map pass in ExtensionManifest._validate().

Common situations: Porting a community extension authored before the namespacing rule; using uppercase or underscore-separated command slugs; copy-pasting a command name from a different extension and forgetting to change the middle segment.

Related errors


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