github/spec-kit · error · ValidationError

Each command entry in 'provides.commands' must be a mapping

Error message

Each command entry in 'provides.commands' must be a mapping

What it means

Every command mapping in provides.commands must contain both name and file keys. name is the slash-command identifier agents invoke; file is the template file installed for it. Either key missing aborts validation of the manifest.

Source

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

                        )
                    if "priority" in entry:
                        priority = entry["priority"]
                        if not isinstance(priority, int) or isinstance(priority, bool):
                            raise ValidationError(
                                f"Hook '{hook_name}' has invalid 'priority': "
                                "must be an integer"
                            )
                        if priority < 1:
                            raise ValidationError(
                                f"Hook '{hook_name}' has invalid 'priority': "
                                "must be >= 1"
                            )

        # Validate commands; track renames so hook references can be rewritten.
        rename_map: Dict[str, str] = {}
        for cmd in commands:
            if not isinstance(cmd, dict):
                raise ValidationError(
                    "Each command entry in 'provides.commands' must be a mapping"
                )
            if "name" not in cmd or "file" not in cmd:
                raise ValidationError("Command missing 'name' or 'file'")
            # The pattern match below would raise a bare TypeError on a
            # non-string name (``name: 2``), escaping the ValidationError
            # contract. The 'file' field needs no check here:
            # relative_extension_path_violation() below already rejects a
            # non-string value.
            if not isinstance(cmd["name"], str):
                raise ValidationError(
                    f"Invalid command name: expected a string, "
                    f"got {type(cmd['name']).__name__}"
                )

            # Validate the 'file' field at manifest-load time using the single
            # shared policy in relative_extension_path_violation(), so manifest
            # validation cannot drift from the runtime registrar guard. This is

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Add both keys: `- name: check\n file: commands/check.md`.
  2. Point file at an existing file inside the extension directory.
  3. Verify key spelling — exact literals `name` and `file` are required.

Example fix

# before
provides:
  commands:
    - name: check

# after
provides:
  commands:
    - name: check
      file: commands/check.md
Defensive patterns

Strategy: validation

Validate before calling

def commands_have_name_and_file(data: dict) -> bool:
    cmds = (data.get("provides") or {}).get("commands") or []
    return all("name" in c and "file" in c for c in cmds if isinstance(c, dict))

Type guard

def is_complete_command_entry(v: object) -> bool:
    return isinstance(v, dict) and isinstance(v.get("name"), str) and "file" in v

Prevention

When it happens

Trigger: A command entry `{name: check}` with no file, or `{file: commands/check.md}` with no name. The `"name" not in cmd or "file" not in cmd` check fires.

Common situations: Author omits file assuming the loader infers it from name, or drops name for a file-only entry. Typos (filename, cmd) also trigger it.

Related errors


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