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
- Declare at least one command, hook, event, template, or script in the manifest.
- If you meant to ship a command, fill in `provides.commands` with a name/file entry pointing at an existing file.
- 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
- Declare at least one command, hook, event, template, or script before installing.
- Don't ship skeleton manifests with all-empty contribution lists.
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
- Missing requires.speckit_version
- Hook '{hook_name}' missing required 'command' field
- Each command entry in 'provides.commands' must be a mapping
- Missing required field: {field}
- Missing extension.{field}
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/507aa443882a74e8.
Report an issue: GitHub.