{"record":{"id":"e5dc6695b577a343","repo":"github/spec-kit","slug":"event-event-name-handler-has-invalid-timeout","errorCode":null,"errorMessage":"Event '{event_name}' handler has invalid 'timeout': must be a positive integer","messagePattern":"Event '(.+?)' handler has invalid 'timeout': must be a positive integer","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/events.py","lineNumber":823,"sourceCode":"    for handler in handlers:\n        command = handler.get(\"command\")\n        if not isinstance(command, str) or not command.strip():\n            raise ValidationError(\n                f\"Event '{event_name}' handler missing required non-empty 'command' string\"\n            )\n        # C10: matcher must be a string (or absent). A non-string matcher such\n        # as `matcher: []` passes extension validation but later crashes\n        # by_matcher.setdefault(matcher, ...) with TypeError: unhashable type.\n        matcher = handler.get(\"matcher\")\n        if matcher is not None and not isinstance(matcher, str):\n            raise ValidationError(\n                f\"Event '{event_name}' handler has invalid 'matcher': \"\n                \"must be a string\"\n            )\n        timeout = handler.get(\"timeout\")\n        if timeout is not None:\n            if not isinstance(timeout, int) or isinstance(timeout, bool) or timeout <= 0:\n                raise ValidationError(\n                    f\"Event '{event_name}' handler has invalid 'timeout': must be a positive integer\"\n                )\n\n\ndef resolve_events(\n    integration_key: str,\n    integration_config: dict[str, Any] | None,\n    project_root: Path,\n    parsed_options: dict[str, Any] | None,\n) -> ResolvedEvents:\n    \"\"\"Resolve the final event set for an integration.\n\n    Returns a mapping of canonical event name → ordered list of handler\n    configs. Layers (lowest → highest precedence):\n\n    1. CLI gate ``--events false`` → empty map (caller still removes prior\n       native hooks; see ``install_integration_events``).\n    2. Built-in defaults from ``integration_config[\"events\"]`` (single-config","sourceCodeStart":805,"sourceCodeEnd":841,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/events.py#L805-L841","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"# before\nhandlers:\n  - command: \"./check.sh\"\n    timeout: \"30\"\n\n# after\nhandlers:\n  - command: \"./check.sh\"\n    timeout: 30","handlingStrategy":"validation","validationCode":"def valid_timeout(t) -> bool:\n    return t is None or (isinstance(t, int) and not isinstance(t, bool) and t > 0)","typeGuard":null,"tryCatchPattern":"except ValidationError as e:\n    if \"timeout\" in str(e):\n        # normalize: coerce numeric strings/floats to int or drop the key\n        handler.pop(\"timeout\", None)","preventionTips":["Write timeouts as bare integers in YAML; never quote them.","Remember bool is rejected: on/yes/true are not valid timeouts."],"tags":["events","validation","timeout","yaml"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}