github/spec-kit · error · ValidationError
Invalid hooks: expected a mapping
Error message
Invalid hooks: expected a mapping
What it means
The optional top-level hooks section must be a mapping keyed by hook/event name, where each value is a single mapping or a list of mappings (normalized by coerce_hook_entries). A non-dict hooks value is rejected before per-hook validation.
Source
Thrown at src/specify_cli/extensions/__init__.py:390
provides = self.data["provides"]
if not isinstance(provides, dict):
raise ValidationError(
f"Invalid provides: expected a mapping, got {type(provides).__name__}"
)
commands = provides.get("commands", [])
templates = provides.get("templates", [])
scripts = provides.get("scripts", [])
hooks = self.data.get("hooks")
events = self.data.get("events")
if "commands" in provides and not isinstance(commands, list):
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")View on GitHub (pinned to bf88c9f9a8)
Solutions
- Rewrite hooks as a mapping: `hooks:\n post-install:\n command: ./script.sh`.
- Remove list dashes at the hooks level; dashes belong only inside a hook's entry list.
- Use a mapping or list-of-mappings for each hook value as per-hook validation expects.
Example fix
# before
hooks:
- post-install:
command: ./setup.sh
# after
hooks:
post-install:
command: ./setup.sh Defensive patterns
Strategy: validation
Validate before calling
def hooks_ok(data: dict) -> bool:
return "hooks" not in data or isinstance(data["hooks"], dict) Type guard
def is_hooks_mapping(data: dict) -> bool:
return "hooks" not in data or isinstance(data["hooks"], dict) Prevention
- hooks is a name-keyed mapping; dashes belong inside a hook's entry list only.
- Each value is a mapping or a list of mappings.
When it happens
Trigger: `hooks:` set to a list of `[event_name, config]` pairs, or a string. isinstance(hooks, dict) fails.
Common situations: Author writes hooks as a YAML list (one dash per hook) instead of a keyed mapping, which is the more common YAML shape for named entries.
Related errors
- Invalid requires: expected a mapping, got {type(requires).__
- Invalid provides: expected a mapping, got {type(provides).__
- Invalid provides.commands: expected a list
- Invalid provides.templates: expected a list
- Invalid provides.scripts: expected a list
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/355da9298205473a.
Report an issue: GitHub.