github/spec-kit · error · ValidationError

Invalid runtimes for script '{name}': expected a list of str

Error message

Invalid runtimes for script '{name}': expected a list of strings

What it means

A provides.scripts entry declared 'runtimes' that is not a list of strings (e.g. a bare string, a dict, or a list containing non-strings). The runtimes field declares which interpreter variants a script ships (bash/powershell/python); only its container shape is checked here — element values are checked next against VALID_SCRIPT_RUNTIMES.

Source

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

                raise ValidationError(f"Invalid {singular} 'file' {label}: {reason}")

            if "description" in entry and not isinstance(entry["description"], str):
                raise ValidationError(
                    f"Invalid {singular} description for '{name}': expected a string"
                )

            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

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Wrap runtimes in a list of strings: "runtimes": ["bash", "python"].
  2. Drop the key if you don't need to declare runtimes explicitly.
  3. Check the neighboring element check: after fixing shape, values must be from {bash, powershell, python}.

Example fix

// before
"runtimes": "bash"
// after
"runtimes": ["bash"]
Defensive patterns

Strategy: type-guard

Validate before calling

for e in manifest.get("provides", {}).get("scripts", []):
    if isinstance(e, dict) and "runtimes" in e:
        r = e["runtimes"]
        assert isinstance(r, list) and all(isinstance(x, str) for x in r), f"bad runtimes for {e.get('name')}: {r!r}"

Type guard

def is_str_list_runtimes(entry: dict) -> bool:
    r = entry.get("runtimes")
    return r is None or (isinstance(r, list) and all(isinstance(x, str) for x in r))

Try / catch

try:
    ExtensionManifest.load(path)
except ValidationError as e:
    if "runtimes" in str(e) and "list of strings" in str(e):
        # wrap runtimes in a list / quote elements, then retry
        ...

Prevention

When it happens

Trigger: "runtimes": "bash" (bare string), "runtimes": {"sh": 1}, or "runtimes": ["bash", 2]. Raised by the isinstance/all() check in the scripts-only branch of the artifact validator.

Common situations: Shorthand single-runtime declaration; YAML flow syntax collapsing a one-element list to a scalar; generated manifests emitting an enum object.

Related errors


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