{"record":{"id":"da7f992d4cf594df","repo":"github/spec-kit","slug":"aliases-for-command-cmd-name-must-be-a-list","errorCode":null,"errorMessage":"Aliases for command '{cmd['name']}' must be a list","messagePattern":"Aliases for command '(.+?)' must be a list","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/extensions/__init__.py","lineNumber":498,"sourceCode":"                        f\"The extension author should update the manifest to use this name.\"\n                    )\n                    rename_map[cmd[\"name\"]] = corrected\n                    cmd[\"name\"] = corrected\n                else:\n                    raise ValidationError(\n                        f\"Invalid command name '{cmd['name']}': \"\n                        \"must follow pattern 'speckit.{extension}.{command}'\"\n                    )\n\n            # Validate alias types; no pattern enforcement on aliases — they are\n            # intentionally free-form to preserve community extension compatibility\n            # (e.g. 'speckit.verify' short aliases used by existing extensions).\n            aliases = cmd.get(\"aliases\")\n            if aliases is None:\n                cmd[\"aliases\"] = []\n                aliases = []\n            if not isinstance(aliases, list):\n                raise ValidationError(\n                    f\"Aliases for command '{cmd['name']}' must be a list\"\n                )\n            for alias in aliases:\n                if not isinstance(alias, str):\n                    raise ValidationError(\n                        f\"Aliases for command '{cmd['name']}' must be strings\"\n                    )\n                alias_reason = relative_extension_path_violation(alias)\n                if alias_reason:\n                    raise ValidationError(\n                        f\"Invalid alias {alias!r} for command \"\n                        f\"'{cmd['name']}': {alias_reason}\"\n                    )\n\n        # Rewrite any hook command references that pointed at a renamed command or\n        # an alias-form ref (ext.cmd → speckit.ext.cmd).  Always emit a warning when\n        # the reference is changed so extension authors know to update the manifest.\n        for hook_name, hook_data in self.data.get(\"hooks\", {}).items():","sourceCodeStart":480,"sourceCodeEnd":516,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/extensions/__init__.py#L480-L516","documentation":"A provides.commands entry declared 'aliases' with a value that is not a YAML/JSON list (e.g. a bare string or mapping). Aliases are optional (missing/null normalizes to []), but when present they must be a list; the manifest loader raises ValidationError before any alias content is examined. Note aliases are intentionally free-form strings (no speckit.* pattern enforced) to keep community extensions working.","triggerScenarios":"\"aliases\": \"build\" (bare string), \"aliases\": {\"short\": \"build\"} (mapping), or \"aliases\": 3. Occurs while iterating commands during ExtensionManifest._validate().","commonSituations":"Author used a single string because there is only one alias; YAML folding a one-element flow into a scalar; copy-pasting from a docs example that shows the alias name instead of a list.","solutions":["Wrap the alias(es) in a list: \"aliases\": [\"build\"].","If no aliases are wanted, remove the key entirely (it defaults to []).","Ensure YAML/JSON serialization of generated manifests always emits arrays for aliases."],"exampleFix":"// before\n\"aliases\": \"build\"\n// after\n\"aliases\": [\"build\"]","handlingStrategy":"validation","validationCode":"for c in manifest.get(\"provides\", {}).get(\"commands\", []):\n    if \"aliases\" in c and c[\"aliases\"] is not None and not isinstance(c[\"aliases\"], list):\n        raise SystemExit(f\"aliases for {c.get('name')} must be a list\")","typeGuard":"def has_list_aliases(cmd: dict) -> bool:\n    a = cmd.get(\"aliases\")\n    return a is None or isinstance(a, list)","tryCatchPattern":"try:\n    ExtensionManifest.load(path)\nexcept ValidationError as e:\n    if \"must be a list\" in str(e):\n        # wrap the aliases value in [ ... ] and retry\n        ...","preventionTips":["Always emit aliases as JSON arrays even for a single value.","Omit the aliases key entirely when unused.","Lint generated manifests for list-typed fields."],"tags":["extensions","manifest","validation","aliases"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}