github/spec-kit · warning · ValidationError

Unknown event '{event_name}': must be one of {sorted(CANONIC

Error message

Unknown event '{event_name}': must be one of {sorted(CANONICAL_EVENTS)}

What it means

Event handler configuration only accepts the canonical snake_case event names: session_start, pre_tool_use, post_tool_use, session_end, user_prompt_submit, and stop. An unknown key raises ValidationError from `_validate_resolved_event`; in the normal resolve_events flow the exception is caught and the entire user YAML override is abandoned with a warning, keeping built-in/extension events.

Source

Thrown at src/specify_cli/events.py:802

        if not isinstance(entry, dict):
            logger.warning("Skipping malformed event handler (expected a mapping): %r", entry)
            continue
        handlers.append(entry)
    return handlers


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")

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Replace the invalid key with one of: session_start, pre_tool_use, post_tool_use, session_end, user_prompt_submit, stop.
  2. Use snake_case exactly; names are case-sensitive.
  3. Re-run integration install/upgrade or event resolution and confirm the warning no longer appears.
  4. If an extension itself declares the bad name, fix its extension.yml events mapping.

Example fix

# .specify/integration-events.yml (before)
integrations:
  claude:
    events:
      SessionStart:
        command: speckit.session-start

# after
integrations:
  claude:
    events:
      session_start:
        command: speckit.session-start
Defensive patterns

Strategy: validation

Validate before calling

from specify_cli.events import CANONICAL_EVENTS

def override_events_are_canonical(override: dict) -> bool:
    integrations = override.get("integrations", {})
    return all(
        event in CANONICAL_EVENTS
        for entry in integrations.values()
        if isinstance(entry, dict)
        for event in entry.get("events", {})
    )

Type guard

from typing import Any, TypeGuard
from specify_cli.events import CANONICAL_EVENTS

def is_canonical_event_name(value: Any) -> TypeGuard[str]:
    return isinstance(value, str) and value in CANONICAL_EVENTS

Try / catch

from specify_cli.extensions import ValidationError

try:
    _validate_resolved_event(event_name, handlers)
except ValidationError as exc:
    if str(exc).startswith("Unknown event"):
        warn_and_ignore_entire_override(event_name)
    else:
        raise

Prevention

When it happens

Trigger: Writing `.specify/integration-events.yml` with a CamelCase native hook name such as SessionStart, a hyphenated name such as pre-tool-use, or an unsupported event name under an integration's `events` mapping.

Common situations: Copying names from Claude Code's native hooks JSON or an agent vendor's documentation instead of Spec Kit's canonical event vocabulary.

Related errors


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