{"record":{"id":"802dc891a3e72702","repo":"github/spec-kit","slug":"invalid-provides-commands-expected-a-list","errorCode":null,"errorMessage":"Invalid provides.commands: expected a list","messagePattern":"Invalid provides\\.commands: expected a list","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/extensions/__init__.py","lineNumber":384,"sourceCode":"            raise ValidationError(\n                \"Invalid requires.speckit_version: expected a non-empty string, \"\n                f\"got {type(requires['speckit_version']).__name__}\"\n            )\n\n        # Validate provides section\n        provides = self.data[\"provides\"]\n        if not isinstance(provides, dict):\n            raise ValidationError(\n                f\"Invalid provides: expected a mapping, got {type(provides).__name__}\"\n            )\n        commands = provides.get(\"commands\", [])\n        templates = provides.get(\"templates\", [])\n        scripts = provides.get(\"scripts\", [])\n        hooks = self.data.get(\"hooks\")\n        events = self.data.get(\"events\")\n\n        if \"commands\" in provides and not isinstance(commands, list):\n            raise ValidationError(\"Invalid provides.commands: expected a list\")\n        if \"templates\" in provides and not isinstance(templates, list):\n            raise ValidationError(\"Invalid provides.templates: expected a list\")\n        if \"scripts\" in provides and not isinstance(scripts, list):\n            raise ValidationError(\"Invalid provides.scripts: expected a list\")\n        if \"hooks\" in self.data and not isinstance(hooks, dict):\n            raise ValidationError(\"Invalid hooks: expected a mapping\")\n        if \"events\" in self.data:\n            from ..events import validate_events\n            validate_events(self.data)\n\n        has_commands = bool(commands)\n        has_hooks = bool(hooks)\n        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(","sourceCodeStart":366,"sourceCodeEnd":402,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/extensions/__init__.py#L366-L402","documentation":"If the provides mapping contains a commands key, its value must be a list. Each list element is a mapping describing one command (name, file); the type check runs before per-entry validation.","triggerScenarios":"`provides:\\n  commands:` followed by a single mapping (not wrapped in a list), or a string like `commands: check`. isinstance(commands, list) fails.","commonSituations":"An extension with exactly one command where the author omits the list dash, so the single entry mapping sits where a list is expected.","solutions":["Wrap command entries in a list: prefix each with `- ` under commands.","Even for a single command, use the list form.","Ensure no stray quoting turns the whole value into a string."],"exampleFix":"# before\nprovides:\n  commands:\n    name: check\n    file: commands/check.md\n\n# after\nprovides:\n  commands:\n    - name: check\n      file: commands/check.md","handlingStrategy":"validation","validationCode":"def commands_list_ok(data: dict) -> bool:\n    provides = data.get(\"provides\", {})\n    return \"commands\" not in provides or isinstance(provides[\"commands\"], list)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Every command entry starts with `- ` even when there is only one.","Keep a list even for a single command; the mapping-only shorthand is invalid."],"tags":["extension-manifest","validation","yaml-structure"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}