github/spec-kit · error · ValidationError

Invalid version: {ext['version']}

Error message

Invalid version: {ext['version']}

What it means

The extension.version field cannot be parsed by packaging.version.Version, so it is not a valid version string. Spec Kit requires PEP 440-compatible versions so compatibility checks (requires.speckit_version ranges, upgrade prompts) can compare versions.

Source

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

                raise ValidationError(f"Missing extension.{field}")
            if not isinstance(ext[field], str):
                raise ValidationError(
                    f"Invalid extension.{field}: expected a string, "
                    f"got {type(ext[field]).__name__}"
                )

        # 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"]

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Use a PEP 440 version such as `version: '0.1.0'` or `version: '2.3.1'`.
  2. If you need semver prereleases, write them PEP 440 style: `1.0.0a1` or `1.0.0-beta.1` (both parse).
  3. Quote the version string in extension.yml so YAML does not coerce it to a float/int.

Example fix

# before
version: latest

# after
version: '0.1.0'
Defensive patterns

Strategy: validation

Validate before calling

from packaging import version

def valid_ext_version(v: object) -> bool:
    if not isinstance(v, str):
        return False
    try:
        version.Version(v)
        return True
    except version.InvalidVersion:
        return False

Type guard

def is_pep440_version(v: object) -> bool:
    try:
        version.Version(v) if isinstance(v, str) else None
        return isinstance(v, str)
    except Exception:
        return False

Try / catch

try:
    ExtensionManifest.load(path)
except ValidationError as e:
    if "Invalid version" in str(e):
        fix_version_field(path)

Prevention

When it happens

Trigger: A manifest declares `version: '1.0'` unquoted-in-invalid-form, `version: latest`, `version: v1.0.0-alpha` with stray text, or `version: 1.0.0.0.1`. pkg_version.Version(ext["version"]) raises InvalidVersion, which is converted to this ValidationError.

Common situations: Using `latest` or a git SHA as version, prefixing with `v`, or YAML auto-parsing `version: 1.0` into a float that stringifies as `1.0` (valid) but `version: 1.0.0.0.1` (invalid PEP 440) failing. Also copy-pasting semver-only prerelease syntax that PEP 440 rejects.

Related errors


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