github/spec-kit · error · ValidationError

Invalid runtimes for script ' ': must be one of

Error message

Invalid runtimes {invalid} for script '{name}': must be one of {sorted(VALID_SCRIPT_RUNTIMES)}

What it means

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.

Solutions

  1. Map short names to canonical ones: sh→bash, ps→powershell, py→python.
  2. Remove runtime identifiers the system does not support (e.g. node).
  3. Re-check with the allowed set printed in the error message: ['bash', 'powershell', 'python'].

Example fix

// before
"runtimes": ["sh", "py"]
// after
"runtimes": ["bash", "python"]
Defensive patterns

Strategy: validation

Validate before calling

VALID = {"bash", "powershell", "python"}
for e in manifest.get("provides", {}).get("scripts", []):
    if isinstance(e, dict) and isinstance(e.get("runtimes"), list):
        invalid = set(e["runtimes"]) - VALID
        assert not invalid, f"invalid runtimes {sorted(invalid)} for {e.get('name')}"

Type guard

def runtimes_all_valid(entry: dict) -> bool:
    VALID = {"bash", "powershell", "python"}
    r = entry.get("runtimes")
    return r is None or (isinstance(r, list) and set(r) <= VALID)

Try / catch

try:
    ExtensionManifest.load(path)
except ValidationError as e:
    if "must be one of" in str(e):
        # map sh->bash, ps->powershell, py->python; drop unsupported values
        ...

Prevention

When it happens

Trigger: "runtimes": ["sh", "py"] or ["powershell", "node"]. Raised by the set-difference check after the list-of-strings shape check in the scripts branch.

Common situations: 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.

Related errors


AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14). Data as JSON: /api/errors/7dfe95f7461d3554. Report an issue: GitHub.

Appendix: source

Thrown at src/specify_cli/extensions/__init__.py:638

                )

            if "strategy" in entry:
                raise ValidationError(
                    f"Invalid {singular} entry '{name}': 'strategy' is not authorable for "
                    "extension-provided artifacts, which always use 'replace' semantics"
                )

            if section == "scripts" and "runtimes" in entry:
                runtimes = entry["runtimes"]
                if not isinstance(runtimes, list) or not all(
                    isinstance(r, str) for r in runtimes
                ):
                    raise ValidationError(
                        f"Invalid runtimes for script '{name}': expected a list of strings"
                    )
                invalid = sorted(set(runtimes) - VALID_SCRIPT_RUNTIMES)
                if invalid:
                    raise ValidationError(
                        f"Invalid runtimes {invalid} for script '{name}': "
                        f"must be one of {sorted(VALID_SCRIPT_RUNTIMES)}"
                    )

    @staticmethod
    def _try_correct_command_name(name: str, ext_id: str) -> Optional[str]:
        """Try to auto-correct a non-conforming command name to the required pattern.

        Handles the two legacy formats used by community extensions:
          - 'speckit.command'  → 'speckit.{ext_id}.command'
          - '{ext_id}.command' → 'speckit.{ext_id}.command'

        The 'X.Y' form is only corrected when X matches ext_id to ensure the
        result passes the install-time namespace check. Any other prefix is
        uncorrectable and will produce a ValidationError at the call site.

        Returns the corrected name, or None if no safe correction is possible.
        """

View on GitHub (pinned to bf88c9f9a8)