{"record":{"id":"dfae657dc2ac066b","repo":"github/spec-kit","slug":"invalid-version-ext-version","errorCode":null,"errorMessage":"Invalid version: {ext['version']}","messagePattern":"Invalid version: (.+?)","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/extensions/__init__.py","lineNumber":326,"sourceCode":"                raise ValidationError(f\"Missing extension.{field}\")\n            if not isinstance(ext[field], str):\n                raise ValidationError(\n                    f\"Invalid extension.{field}: expected a string, \"\n                    f\"got {type(ext[field]).__name__}\"\n                )\n\n        # Validate extension ID format\n        if not re.match(r\"^[a-z0-9-]+$\", ext[\"id\"]):\n            raise ValidationError(\n                f\"Invalid extension ID '{ext['id']}': \"\n                \"must be lowercase alphanumeric with hyphens only\"\n            )\n\n        # Validate semantic version\n        try:\n            pkg_version.Version(ext[\"version\"])\n        except pkg_version.InvalidVersion:\n            raise ValidationError(f\"Invalid version: {ext['version']}\")\n\n        # Validate optional category field (free-form string)\n        if \"category\" in ext:\n            if not isinstance(ext[\"category\"], str) or not ext[\"category\"].strip():\n                raise ValidationError(\n                    \"Invalid extension.category: must be a non-empty string\"\n                )\n\n        # Validate optional effect field\n        if \"effect\" in ext:\n            if not isinstance(ext[\"effect\"], str) or ext[\"effect\"] not in VALID_EFFECTS:\n                raise ValidationError(\n                    f\"Invalid extension.effect '{ext.get('effect')}': \"\n                    f\"must be one of {sorted(VALID_EFFECTS)}\"\n                )\n\n        # Validate requires section\n        requires = self.data[\"requires\"]","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/extensions/__init__.py#L308-L344","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use a PEP 440 version such as `version: '0.1.0'` or `version: '2.3.1'`.","If you need semver prereleases, write them PEP 440 style: `1.0.0a1` or `1.0.0-beta.1` (both parse).","Quote the version string in extension.yml so YAML does not coerce it to a float/int."],"exampleFix":"# before\nversion: latest\n\n# after\nversion: '0.1.0'","handlingStrategy":"validation","validationCode":"from packaging import version\n\ndef valid_ext_version(v: object) -> bool:\n    if not isinstance(v, str):\n        return False\n    try:\n        version.Version(v)\n        return True\n    except version.InvalidVersion:\n        return False","typeGuard":"def is_pep440_version(v: object) -> bool:\n    try:\n        version.Version(v) if isinstance(v, str) else None\n        return isinstance(v, str)\n    except Exception:\n        return False","tryCatchPattern":"try:\n    ExtensionManifest.load(path)\nexcept ValidationError as e:\n    if \"Invalid version\" in str(e):\n        fix_version_field(path)","preventionTips":["Always quote version strings in YAML to prevent float coercion.","Run `python -c \"from packaging.version import Version; Version(open('extension.yml').read())\"`-style checks in a pre-commit hook."],"tags":["extension-manifest","validation","versioning"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}