github/spec-kit · error · ValidationError

Extension must provide at least one command, hook, or event

Error message

Extension must provide at least one command, hook, or event (or a declared template/script)

What it means

An extension manifest must declare at least one contribution: a non-empty provides.commands, hooks, events, provides.templates, or provides.scripts. An extension that provides nothing is considered malformed — there is nothing to install or dispatch.

Source

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

            raise ValidationError("Invalid provides.commands: expected a list")
        if "templates" in provides and not isinstance(templates, list):
            raise ValidationError("Invalid provides.templates: expected a list")
        if "scripts" in provides and not isinstance(scripts, list):
            raise ValidationError("Invalid provides.scripts: expected a list")
        if "hooks" in self.data and not isinstance(hooks, dict):
            raise ValidationError("Invalid hooks: expected a mapping")
        if "events" in self.data:
            from ..events import validate_events
            validate_events(self.data)

        has_commands = bool(commands)
        has_hooks = bool(hooks)
        has_events = bool(events)
        has_templates = bool(templates)
        has_scripts = bool(scripts)

        if not has_commands and not has_hooks and not has_events and not has_templates and not has_scripts:
            raise ValidationError(
                "Extension must provide at least one command, hook, or event "
                "(or a declared template/script)"
            )

        self._validate_provided_artifacts(templates, section="templates", singular="template")
        self._validate_provided_artifacts(scripts, section="scripts", singular="script")

        # Validate hook values (if present).
        # Each event is a single mapping or a list of mappings.
        if hooks:
            for hook_name, hook_config in hooks.items():
                if isinstance(hook_config, list) and not hook_config:
                    raise ValidationError(
                        f"Invalid hook '{hook_name}': list must contain at least one entry"
                    )
                for entry in coerce_hook_entries(hook_config):
                    if not isinstance(entry, dict):
                        raise ValidationError(

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Declare at least one command, hook, event, template, or script in the manifest.
  2. If you meant to ship a command, fill in `provides.commands` with a name/file entry pointing at an existing file.
  3. If the extension is events-only, ensure the events key is present and non-empty (events feed has_events).

Example fix

# before
provides:
  commands: []

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

Strategy: validation

Validate before calling

def has_any_contribution(data: dict) -> bool:
    provides = data.get("provides", {})
    return bool(
        provides.get("commands")
        or provides.get("templates")
        or provides.get("scripts")
        or data.get("hooks")
        or data.get("events")
    )

Prevention

When it happens

Trigger: A manifest where commands/templates/scripts lists are all empty or absent, and hooks/events are missing: all five has_* flags are falsy, so the combined check fires.

Common situations: Skeleton manifests generated from a template with empty lists, or an author commenting out all contributions while developing.

Related errors


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