github/spec-kit · error · ValidationError

Hook '{hook_name}' has invalid 'priority': must be >= 1

Error message

Hook '{hook_name}' has invalid 'priority': must be >= 1

What it means

Every element of provides.commands must be a mapping (dict) describing one command (with name and file keys). A non-dict element — string, number, list — is rejected before any key checks run.

Source

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

                for entry in coerce_hook_entries(hook_config):
                    if not isinstance(entry, dict):
                        raise ValidationError(
                            f"Invalid hook '{hook_name}': "
                            "expected a mapping or list of mappings"
                        )
                    if not entry.get("command"):
                        raise ValidationError(
                            f"Hook '{hook_name}' missing required 'command' field"
                        )
                    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.

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Make each entry a mapping: `- name: check\n file: commands/check.md`.
  2. Provide both name and file for every command.
  3. Avoid shorthand — the schema has no string-only command form.

Example fix

# before
provides:
  commands:
    - check

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

Strategy: type-guard

Validate before calling

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

Type guard

def is_command_entry(v: object) -> bool:
    return isinstance(v, dict) and "name" in v and "file" in v

Prevention

When it happens

Trigger: `provides:\n commands:\n - check` — a bare string element instead of a mapping. isinstance(cmd, dict) fails on the first loop iteration.

Common situations: Author writes a shorthand list of command names assuming the file is derived, or a malformed list-of-lists from a bad merge.

Related errors


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