github/spec-kit · error · ValidationError

Missing requires.speckit_version

Error message

Missing requires.speckit_version

What it means

The requires mapping must contain the key speckit_version. This is the only required entry in requires and is what check_compatibility() later feeds into packaging SpecifierSet to decide whether the installed Spec Kit version can run the extension.

Source

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

                    "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:
            raise ValidationError("Missing requires.speckit_version")
        # Presence alone is not enough: check_compatibility() feeds this value to
        # ``SpecifierSet(required)``, guarded only by ``except InvalidSpecifier``,
        # which a non-string escapes two different ways. A float/int/bool/None
        # raises TypeError from the constructor, while a list or dict is an
        # *iterable*, so SpecifierSet accepts it and the failure surfaces much
        # later as ``AttributeError: 'str' object has no attribute 'filter'`` from
        # inside .contains(). Neither is a CompatibilityError, so both bypass the
        # CLI's "Compatibility Error" handler and exit 1 with a raw traceback
        # naming no field. An unquoted ``speckit_version: 1.0`` is an easy YAML
        # slip. Mirrors the sibling IntegrationDescriptor, which already requires
        # a non-empty string here.
        if (
            not isinstance(requires["speckit_version"], str)
            or not requires["speckit_version"].strip()
        ):
            raise ValidationError(
                "Invalid requires.speckit_version: expected a non-empty string, "
                f"got {type(requires['speckit_version']).__name__}"

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Add `speckit_version` under requires with a PEP 440 specifier string, e.g. `speckit_version: ">=1.0.0"`.
  2. Fix the key spelling — it must be exactly speckit_version, snake_case.
  3. Pick the specifier from your extension's actual minimum supported Spec Kit version.

Example fix

# before
requires:
  speckit-ver: '>=1.0.0'

# after
requires:
  speckit_version: ">=1.0.0"
Defensive patterns

Strategy: validation

Validate before calling

def has_speckit_version(data: dict) -> bool:
    return "speckit_version" in data.get("requires", {})

Prevention

When it happens

Trigger: A manifest has `requires: {}`, or a misspelled key like `requires:\n speckit-ver: '>=1'` or `specify_version:`. The membership test `"speckit_version" not in requires` fires.

Common situations: Typos in the key (speckit-ver, speckitversion, specify_version), or an empty requires block left from a template.

Related errors


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