github/spec-kit · error · PresetCompatibilityError

Invalid version specifier

Error message

Invalid version specifier: {required}

What it means

check_compatibility could not parse the requires.speckit_version string as a PEP 440 specifier set — packaging.SpecifierSet raised InvalidSpecifier. The value is a string but syntactically invalid, e.g. missing an operator or using unsupported syntax.

Solutions

  1. Use valid PEP 440 specifier syntax: ">=1.0.0", "~=1.2", or comma-separated ">=1.0,<2.0".
  2. Replace npm-style ^/~ ranges with PEP 440 equivalents (~=x.y.z).
  3. Test your specifier quickly: `python -c "from packaging.specifiers import SpecifierSet; SpecifierSet('>=1.0')"`.

Example fix

# before (preset.yml)
requires:
  speckit_version: "^1.2"

# after
requires:
  speckit_version: ">=1.2,<2.0"
Defensive patterns

Strategy: validation

Validate before calling

from packaging.specifiers import SpecifierSet, InvalidSpecifier
try:
    SpecifierSet(required)
except InvalidSpecifier:
    raise ValueError(f"bad specifier {required!r}: use PEP 440 like '>=1.0,<2.0'")

Type guard

from packaging.specifiers import SpecifierSet, InvalidSpecifier
def is_valid_specifier(s: str) -> bool:
    try:
        SpecifierSet(s)
        return True
    except InvalidSpecifier:
        return False

Try / catch

try:
    manager.check_compatibility(manifest, speckit_version)
except PresetCompatibilityError as e:
    if "Invalid version specifier" in str(e):
        # rewrite requires.speckit_version in valid PEP 440 syntax and retry
        ...

Prevention

When it happens

Trigger: requires.speckit_version set to something like "1.0" (bare version with no operator), "==>2.0", or "version>=2". SpecifierSet requires operator-prefixed clauses such as ">=1.0,<2.0".

Common situations: Writing just the version number instead of a specifier, or mixing npm-style syntax (^1.2, ~2.1) — PEP 440 does not support caret/tilde ranges.

Related errors


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

Appendix: source

Thrown at src/specify_cli/presets/__init__.py:815

            PresetCompatibilityError: If pack is incompatible
        """
        required = manifest.requires_speckit_version
        # Defense in depth: the manifest validator now rejects a non-string
        # requires.speckit_version, but this method is public and also reachable
        # with a hand-built manifest object. ``InvalidSpecifier`` alone does not
        # cover a non-string -- scalars raise TypeError from the constructor, and
        # a list/dict is iterable so it constructs here and only breaks inside
        # .contains(). Reject up front so this always reports a
        # PresetCompatibilityError.
        if not isinstance(required, str):
            raise PresetCompatibilityError(
                "Invalid version specifier: expected a string, got "
                f"{type(required).__name__} ({required!r})"
            )
        try:
            SpecifierSet(required)  # Just to validate
        except InvalidSpecifier:
            raise PresetCompatibilityError(f"Invalid version specifier: {required}")

        if not version_satisfies(speckit_version, required):
            raise PresetCompatibilityError(
                f"Preset requires spec-kit {required}, "
                f"but {speckit_version} is installed.\n"
                f"Upgrade spec-kit with: {REINSTALL_COMMAND}"
            )

        return True

    def _register_commands(
        self,
        manifest: PresetManifest,
        preset_dir: Path
    ) -> Dict[str, List[str]]:
        """Register preset command overrides with all detected AI agents.

        Scans the preset's templates for type "command", reads each command

View on GitHub (pinned to bf88c9f9a8)