github/spec-kit · error · ValidationError
Event '{event_name}' handler has invalid 'timeout': must be
Error message
Event '{event_name}' handler has invalid 'timeout': must be a positive integer What it means
Raised while resolving an integration's event handlers: a handler entry supplied a 'timeout' key that is not a positive integer. Booleans are explicitly rejected (`isinstance(timeout, bool)` guard) because bool is a subclass of int in Python, and zero/negative values would make no sense for a process timeout.
Source
Thrown at src/specify_cli/events.py:823
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"
)
def resolve_events(
integration_key: str,
integration_config: dict[str, Any] | None,
project_root: Path,
parsed_options: dict[str, Any] | None,
) -> ResolvedEvents:
"""Resolve the final event set for an integration.
Returns a mapping of canonical event name → ordered list of handler
configs. Layers (lowest → highest precedence):
1. CLI gate ``--events false`` → empty map (caller still removes prior
native hooks; see ``install_integration_events``).
2. Built-in defaults from ``integration_config["events"]`` (single-configView on GitHub (pinned to bf88c9f9a8)
Solutions
- Set timeout to a bare positive integer, e.g. `timeout: 30`.
- Remove the timeout key to use the integration's default timeout.
- Check for accidental YAML type coercion (quotes make a string; `.0` makes a float; on/off style words make booleans).
Example fix
# before
handlers:
- command: "./check.sh"
timeout: "30"
# after
handlers:
- command: "./check.sh"
timeout: 30 Defensive patterns
Strategy: validation
Validate before calling
def valid_timeout(t) -> bool:
return t is None or (isinstance(t, int) and not isinstance(t, bool) and t > 0) Try / catch
except ValidationError as e:
if "timeout" in str(e):
# normalize: coerce numeric strings/floats to int or drop the key
handler.pop("timeout", None) Prevention
- Write timeouts as bare integers in YAML; never quote them.
- Remember bool is rejected: on/yes/true are not valid timeouts.
When it happens
Trigger: A handler entry in an extension's events config contains `timeout: 10.5`, `timeout: "30"`, `timeout: -1`, `timeout: 0`, or `timeout: true`; resolve_events() handler validation raises ValidationError before native config is rendered.
Common situations: Writing a YAML float (`timeout: 30.0`), quoting the number (`timeout: "30"`), or using `timeout: true` expecting it to mean enabled; porting timeout values from another format that stores seconds as strings.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Unknown event '{event_name}': must be one of {sorted(CANONIC
- Event '{event_name}' handler missing required non-empty 'com
- Event '{event_name}' handler has invalid 'matcher': 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/e5dc6695b577a343.
Report an issue: GitHub.