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 theView on GitHub (pinned to bf88c9f9a8)
Solutions
- Wrap runtimes in a list of strings: "runtimes": ["bash", "python"].
- Drop the key if you don't need to declare runtimes explicitly.
- 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
- Always emit runtimes as a JSON array of strings.
- Omit the key when not declaring runtimes.
- Remember values must then be bash/powershell/python.
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
- Invalid command name: expected a string, got {type(cmd['name
- Aliases for command '{cmd['name']}' must be strings
- Each entry in 'provides.{section}' must be a mapping
- Invalid {singular} name: expected a string, got {type(name).
- Invalid {singular} description for '{name}': expected a stri
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/139b5f67a1429dee.
Report an issue: GitHub.