github/spec-kit · error · ValidationError

Invalid extension.category: must be a non-empty string

Error message

Invalid extension.category: must be a non-empty string

What it means

The optional extension.category field, when present, must be a non-empty string (after strip). It is free-form (any non-blank text is accepted) but a blank string, null-adjacent value, or non-string (list, number) fails validation.

Source

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

                )

        # Validate extension ID format
        if not re.match(r"^[a-z0-9-]+$", ext["id"]):
            raise ValidationError(
                f"Invalid extension ID '{ext['id']}': "
                "must be lowercase alphanumeric with hyphens only"
            )

        # Validate semantic version
        try:
            pkg_version.Version(ext["version"])
        except pkg_version.InvalidVersion:
            raise ValidationError(f"Invalid version: {ext['version']}")

        # Validate optional category field (free-form string)
        if "category" in ext:
            if not isinstance(ext["category"], str) or not ext["category"].strip():
                raise ValidationError(
                    "Invalid extension.category: must be a non-empty string"
                )

        # Validate optional effect field
        if "effect" in ext:
            if not isinstance(ext["effect"], str) or ext["effect"] not in VALID_EFFECTS:
                raise ValidationError(
                    f"Invalid extension.effect '{ext.get('effect')}': "
                    f"must be one of {sorted(VALID_EFFECTS)}"
                )

        # Validate requires section
        requires = self.data["requires"]
        if not isinstance(requires, dict):
            raise ValidationError(
                f"Invalid requires: expected a mapping, got {type(requires).__name__}"
            )
        if "speckit_version" not in requires:

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Provide a meaningful non-empty category, e.g. `category: tooling`.
  2. Or delete the category key entirely — it is optional and absence passes validation.
  3. Ensure the value is a plain quoted string, not a list or blank.

Example fix

# before
category: ""

# after
category: tooling
Defensive patterns

Strategy: validation

Validate before calling

def valid_category(cat: object) -> bool:
    return not isinstance(cat, str) or bool(cat.strip())  # absent is OK; if present must be non-blank str

def category_ok(data: dict) -> bool:
    cat = data.get("extension", {}).get("category")
    return cat is None or (isinstance(cat, str) and cat.strip())

Type guard

def is_nonempty_str(v: object) -> bool:
    return isinstance(v, str) and bool(v.strip())

Prevention

When it happens

Trigger: A manifest contains `category: ""`, `category: " "`, or `category: [tooling]`. The isinstance/strip check fails and ValidationError is raised during manifest load.

Common situations: Templates with a placeholder `category: ""` left unfilled, YAML flow syntax accidentally making category a list, or a commented-out category where a stray empty value remains.

Related errors


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