github/spec-kit · error · ValidationError

Invalid {singular} description for '{name}': expected a stri

Error message

Invalid {singular} description for '{name}': expected a string

What it means

A provides.templates/scripts entry declared an optional 'description' whose value is not a string. Description is optional (absent is fine), but when present it must be a str — used for listing/help output, so ints, lists, or null are rejected with this ValidationError.

Source

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

            if not VALID_EXTENSION_ARTIFACT_NAME_PATTERN.match(name):
                raise ValidationError(
                    f"Invalid {singular} name '{name}': "
                    "must be lowercase alphanumeric with hyphens only"
                )
            if name in seen_names:
                raise ValidationError(
                    f"Duplicate {singular} name '{name}' in 'provides.{section}'"
                )
            seen_names.add(name)

            file_value = entry["file"]
            reason = relative_extension_path_violation(file_value)
            if reason:
                label = repr(file_value) if isinstance(file_value, str) else f"for {singular} '{name}'"
                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)

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Make the description a quoted string: "description": "Renders the plan document".
  2. Or remove the 'description' key entirely if you have nothing to say.
  3. In generated manifests, wrap descriptions in str() and skip None.

Example fix

// before
{ "name": "plan", "file": "templates/plan.md", "description": 5 }
// after
{ "name": "plan", "file": "templates/plan.md", "description": "Renders the plan document" }
Defensive patterns

Strategy: type-guard

Validate before calling

for section in ("templates", "scripts"):
    for e in manifest.get("provides", {}).get(section, []):
        if isinstance(e, dict) and "description" in e:
            assert isinstance(e["description"], str), f"non-string description for {e.get('name')}"

Type guard

def has_str_description(entry: dict) -> bool:
    return "description" not in entry or isinstance(entry["description"], str)

Try / catch

try:
    ExtensionManifest.load(path)
except ValidationError as e:
    if "description" in str(e) and "expected a string" in str(e):
        # quote the description or drop the key, then retry
        ...

Prevention

When it happens

Trigger: {"name": "plan", "file": "...", "description": 5} or "description": ["a", "b"] or null. Raised by the isinstance check after path validation in the artifact validator.

Common situations: Template variables interpolating an empty/non-string default; YAML unquoted number as description; copying rich metadata from another schema.

Related errors


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