github/spec-kit · warning · ValidationError
Event '{event_name}' handler missing required non-empty 'com
Error message
Event '{event_name}' handler missing required non-empty 'command' string What it means
Every event handler mapping must contain a `command` that is a non-empty string after stripping whitespace. A missing, null, numeric, list-valued, empty, or whitespace-only command raises ValidationError. resolve_events catches this for user overrides and ignores the entire override with a warning, so hooks from lower layers remain active.
Source
Thrown at src/specify_cli/events.py:808
def _validate_resolved_event(event_name: str, handlers: list[dict[str, Any]]) -> None:
"""Validate a resolved event's handlers, raising a user-facing error.
Raised for structural problems the user must fix (unknown event name,
handler missing a ``command``, or ``command`` not a non-empty string per
#17). Malformed-but-skipable entries are already dropped by
``_normalize_handlers``.
"""
from .extensions import ValidationError
if event_name not in CANONICAL_EVENTS:
raise ValidationError(
f"Unknown event '{event_name}': must be one of {sorted(CANONICAL_EVENTS)}"
)
for handler in handlers:
command = handler.get("command")
if not isinstance(command, str) or not command.strip():
raise ValidationError(
f"Event '{event_name}' handler missing required non-empty 'command' string"
)
# C10: matcher must be a string (or absent). A non-string matcher such
# as `matcher: []` passes extension validation but later crashes
# by_matcher.setdefault(matcher, ...) with TypeError: unhashable type.
matcher = handler.get("matcher")
if matcher is not None and not isinstance(matcher, str):
raise ValidationError(
f"Event '{event_name}' handler has invalid 'matcher': "
"must be a string"
)
timeout = handler.get("timeout")
if timeout is not None:
if not isinstance(timeout, int) or isinstance(timeout, bool) or timeout <= 0:
raise ValidationError(
f"Event '{event_name}' handler has invalid 'timeout': must be a positive integer"
)
View on GitHub (pinned to bf88c9f9a8)
Solutions
- Find the event named in the error and ensure each handler mapping has `command: <non-empty-string>`.
- Fix indentation so command belongs to the handler mapping, not the event key.
- Use the canonical command/template name expected by the integration, for example `speckit.session-start`.
- Remove placeholder handlers until a real command is available.
Example fix
# before
integrations:
claude:
events:
session_start:
matcher: "*"
# after
integrations:
claude:
events:
session_start:
command: speckit.session-start
matcher: "*" Defensive patterns
Strategy: validation
Validate before calling
def handlers_have_nonempty_commands(handlers: list[dict]) -> bool:
for handler in handlers:
command = handler.get("command")
if not isinstance(command, str) or not command.strip():
return False
return True Type guard
from typing import Any, TypeGuard
def is_valid_event_handler(value: Any) -> TypeGuard[dict[str, Any]]:
command = value.get("command") if isinstance(value, dict) else None
return isinstance(command, str) and bool(command.strip()) Try / catch
from specify_cli.extensions import ValidationError
try:
_validate_resolved_event(event_name, handlers)
except ValidationError as exc:
if "missing required non-empty 'command'" in str(exc):
warn_and_ignore_entire_override(event_name)
else:
raise Prevention
- Template integration-events.yml from a known-good example and fill every command.
- Validate event YAML in editor/CI before integration upgrade.
- Remember a single invalid handler invalidates the whole integration override.
When it happens
Trigger: A handler in `.specify/integration-events.yml` omits `command`, writes `command: ""`, uses a script/matcher key without command, or sets a non-string command value.
Common situations: YAML indentation places `command` under the wrong mapping, a template variable was never filled in, or the user assumes `script:`/`template:` is accepted instead of `command:`.
Related errors
- Unknown event '{event_name}': must be one of {sorted(CANONIC
- Event '{event_name}' handler has invalid 'matcher': must be
- Event '{event_name}' handler has invalid 'timeout': must be
- Invalid events: expected a mapping
- Invalid event '{event_name}': expected a mapping
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/f95b909a86e2484a.
Report an issue: GitHub.