github/spec-kit · error · ValidationError
Event '{event_name}' missing required 'command' string
Error message
Event '{event_name}' missing required 'command' string What it means
Thrown by validate_events() when an event entry's `command` is missing, empty, whitespace-only, or not a string (issue #17). The strict isinstance check exists because a truthy non-string like `command: [foo]` would pass a bare truthiness check and later render into invalid native agent configuration.
Source
Thrown at src/specify_cli/events.py:1793
def validate_events(data: dict[str, Any]) -> None:
"""Validate ``events`` field in extension manifest data."""
from .extensions import ValidationError
events = data.get("events")
if "events" in data and not isinstance(events, dict):
raise ValidationError("Invalid events: expected a mapping")
if events:
for event_name, event_config in events.items():
if not isinstance(event_config, dict):
raise ValidationError(
f"Invalid event '{event_name}': expected a mapping"
)
command = event_config.get("command")
# #17: command must be a non-empty string. A truthy non-string
# (e.g. command: [foo]) would pass a bare truthiness check and
# later render into invalid native configuration.
if not isinstance(command, str) or not command.strip():
raise ValidationError(
f"Event '{event_name}' missing required 'command' string"
)
if event_name not in CANONICAL_EVENTS:
raise ValidationError(
f"Unknown event '{event_name}': "
f"must be one of {sorted(CANONICAL_EVENTS)}"
)
# C10: matcher must be a string (or absent). A non-string matcher
# such as `matcher: []` would later crash by_matcher.setdefault.
matcher = event_config.get("matcher")
if matcher is not None and not isinstance(matcher, str):
raise ValidationError(
f"Event '{event_name}' has invalid 'matcher': must be a string"
)
timeout = event_config.get("timeout")
if timeout is not None:
if not isinstance(timeout, int) or isinstance(timeout, bool) or timeout <= 0:
raise ValidationError(View on GitHub (pinned to bf88c9f9a8)
Solutions
- Set `command` to a non-empty string, e.g. `command: "./scripts/hook.sh"`.
- If the event is not ready, remove the whole event entry from the manifest rather than leaving a blank command.
- Re-run the extension install/refresh.
Example fix
# before
events:
pre_tool_use:
command:
# after
events:
pre_tool_use:
command: "./scripts/hook.sh" Defensive patterns
Strategy: validation
Validate before calling
cmd = event_config.get("command")
if not isinstance(cmd, str) or not cmd.strip():
raise SystemExit("each event needs a non-empty string 'command'") Type guard
def has_valid_command(cfg: dict) -> bool:
cmd = cfg.get("command")
return isinstance(cmd, str) and bool(cmd.strip()) Try / catch
except ValidationError as e:
if "missing required 'command'" in str(e):
remove_or_fill_event(event_name) Prevention
- Fill in command before installing; remove unfinished event entries instead of stubbing them.
- Quote commands; avoid YAML list syntax for a single command.
When it happens
Trigger: Manifest event config contains `command:` (null), `command: ""`, `command: " "`, or `command: [./run.sh]`; validate_events raises during manifest load/validation.
Common situations: Leaving command blank while drafting the manifest; YAML unquoted `command: [foo]` parsing as a list; using a placeholder like `command: TODO` that is fine, vs an empty stub that is not.
Related errors
- Invalid events: expected a mapping
- Invalid event '{event_name}': expected a mapping
- Event '{event_name}' has invalid 'matcher': must be a string
- Event '{event_name}' has invalid 'timeout': must be a positi
- Manifest must be a YAML mapping at the top level.
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/94b31adf46bcddd4.
Report an issue: GitHub.