{"record":{"id":"7dfe95f7461d3554","repo":"github/spec-kit","slug":"invalid-runtimes-invalid-for-script-name-mu","errorCode":null,"errorMessage":"Invalid runtimes {invalid} for script '{name}': must be one of {sorted(VALID_SCRIPT_RUNTIMES)}","messagePattern":"Invalid runtimes (.+?) for script '(.+?)': must be one of (.+?)","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/extensions/__init__.py","lineNumber":638,"sourceCode":"                )\n\n            if \"strategy\" in entry:\n                raise ValidationError(\n                    f\"Invalid {singular} entry '{name}': 'strategy' is not authorable for \"\n                    \"extension-provided artifacts, which always use 'replace' semantics\"\n                )\n\n            if section == \"scripts\" and \"runtimes\" in entry:\n                runtimes = entry[\"runtimes\"]\n                if not isinstance(runtimes, list) or not all(\n                    isinstance(r, str) for r in runtimes\n                ):\n                    raise ValidationError(\n                        f\"Invalid runtimes for script '{name}': expected a list of strings\"\n                    )\n                invalid = sorted(set(runtimes) - VALID_SCRIPT_RUNTIMES)\n                if invalid:\n                    raise ValidationError(\n                        f\"Invalid runtimes {invalid} for script '{name}': \"\n                        f\"must be one of {sorted(VALID_SCRIPT_RUNTIMES)}\"\n                    )\n\n    @staticmethod\n    def _try_correct_command_name(name: str, ext_id: str) -> Optional[str]:\n        \"\"\"Try to auto-correct a non-conforming command name to the required pattern.\n\n        Handles the two legacy formats used by community extensions:\n          - 'speckit.command'  → 'speckit.{ext_id}.command'\n          - '{ext_id}.command' → 'speckit.{ext_id}.command'\n\n        The 'X.Y' form is only corrected when X matches ext_id to ensure the\n        result passes the install-time namespace check. Any other prefix is\n        uncorrectable and will produce a ValidationError at the call site.\n\n        Returns the corrected name, or None if no safe correction is possible.\n        \"\"\"","sourceCodeStart":620,"sourceCodeEnd":656,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/extensions/__init__.py#L620-L656","documentation":"A provides.scripts entry declared 'runtimes' values outside VALID_SCRIPT_RUNTIMES = {\"bash\", \"powershell\", \"python\"} (extensions/__init__.py:69). The invalid values are collected (deduplicated, sorted) into the message along with the allowed set. These three map to Spec Kit's script-type contract (--script sh|ps|py); older names like 'sh', 'ps', 'shell', or 'pwsh' are not accepted.","triggerScenarios":"\"runtimes\": [\"sh\", \"py\"] or [\"powershell\", \"node\"]. Raised by the set-difference check after the list-of-strings shape check in the scripts branch.","commonSituations":"Using the --script flag short names (sh/ps/py) as runtime values; declaring 'node' or 'ruby' for a helper script; assuming PowerShell's executable name 'pwsh' is the identifier.","solutions":["Map short names to canonical ones: sh→bash, ps→powershell, py→python.","Remove runtime identifiers the system does not support (e.g. node).","Re-check with the allowed set printed in the error message: ['bash', 'powershell', 'python']."],"exampleFix":"// before\n\"runtimes\": [\"sh\", \"py\"]\n// after\n\"runtimes\": [\"bash\", \"python\"]","handlingStrategy":"validation","validationCode":"VALID = {\"bash\", \"powershell\", \"python\"}\nfor e in manifest.get(\"provides\", {}).get(\"scripts\", []):\n    if isinstance(e, dict) and isinstance(e.get(\"runtimes\"), list):\n        invalid = set(e[\"runtimes\"]) - VALID\n        assert not invalid, f\"invalid runtimes {sorted(invalid)} for {e.get('name')}\"","typeGuard":"def runtimes_all_valid(entry: dict) -> bool:\n    VALID = {\"bash\", \"powershell\", \"python\"}\n    r = entry.get(\"runtimes\")\n    return r is None or (isinstance(r, list) and set(r) <= VALID)","tryCatchPattern":"try:\n    ExtensionManifest.load(path)\nexcept ValidationError as e:\n    if \"must be one of\" in str(e):\n        # map sh->bash, ps->powershell, py->python; drop unsupported values\n        ...","preventionTips":["Canonical runtime names are bash, powershell, python — not sh/ps/py.","The allowed set is printed in the error message; align exactly.","Lint runtimes values against VALID_SCRIPT_RUNTIMES in CI."],"tags":["extensions","manifest","scripts","validation"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}