{"record":{"id":"dd810b6afedbfe3f","repo":"github/spec-kit","slug":"invalid-hook-hook-name-list-must-contain-at-l","errorCode":null,"errorMessage":"Invalid hook '{hook_name}': list must contain at least one entry","messagePattern":"Invalid hook '(.+?)': list must contain at least one entry","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/extensions/__init__.py","lineNumber":415,"sourceCode":"        has_events = bool(events)\n        has_templates = bool(templates)\n        has_scripts = bool(scripts)\n\n        if not has_commands and not has_hooks and not has_events and not has_templates and not has_scripts:\n            raise ValidationError(\n                \"Extension must provide at least one command, hook, or event \"\n                \"(or a declared template/script)\"\n            )\n\n        self._validate_provided_artifacts(templates, section=\"templates\", singular=\"template\")\n        self._validate_provided_artifacts(scripts, section=\"scripts\", singular=\"script\")\n\n        # Validate hook values (if present).\n        # Each event is a single mapping or a list of mappings.\n        if hooks:\n            for hook_name, hook_config in hooks.items():\n                if isinstance(hook_config, list) and not hook_config:\n                    raise ValidationError(\n                        f\"Invalid hook '{hook_name}': list must contain at least one entry\"\n                    )\n                for entry in coerce_hook_entries(hook_config):\n                    if not isinstance(entry, dict):\n                        raise ValidationError(\n                            f\"Invalid hook '{hook_name}': \"\n                            \"expected a mapping or list of mappings\"\n                        )\n                    if not entry.get(\"command\"):\n                        raise ValidationError(\n                            f\"Hook '{hook_name}' missing required 'command' field\"\n                        )\n                    if \"priority\" in entry:\n                        priority = entry[\"priority\"]\n                        if not isinstance(priority, int) or isinstance(priority, bool):\n                            raise ValidationError(\n                                f\"Hook '{hook_name}' has invalid 'priority': \"\n                                \"must be an integer\"","sourceCodeStart":397,"sourceCodeEnd":433,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/extensions/__init__.py#L397-L433","documentation":"When a hook's value is a list, that list must contain at least one entry. An explicitly empty list (`hook-name: []`) is treated as a declaration with no behavior, which validation rejects rather than silently installing a no-op hook.","triggerScenarios":"`hooks:\\n  post-install: []` — hook_config is a list and `not hook_config` is true, so the error names the hook.","commonSituations":"Author disables a hook by emptying its list instead of deleting the key, or a generator template emits empty lists for unconfigured hooks.","solutions":["Delete the hook key entirely if you don't want the hook.","Or populate the list with at least one mapping: `- command: ./script.sh`.","Don't use an empty list as a 'disabled' marker — the schema has no such concept."],"exampleFix":"# before\nhooks:\n  post-install: []\n\n# after (omit it, or provide an entry)\n# hooks:\n#   post-install: []\n# or\nhooks:\n  post-install:\n    - command: ./setup.sh","handlingStrategy":"validation","validationCode":"def hook_lists_nonempty(data: dict) -> bool:\n    hooks = data.get(\"hooks\") or {}\n    return all(not isinstance(v, list) or len(v) > 0 for v in hooks.values())","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Delete a hook key to disable it; empty lists are invalid, not 'off'.","When templating manifests, strip unused hooks rather than emitting `[]`."],"tags":["extension-manifest","validation","hooks"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}