{"record":{"id":"ec6c033f72af672c","repo":"github/spec-kit","slug":"invalid-version-specifier-required-ec6c03","errorCode":null,"errorMessage":"Invalid version specifier: {required}","messagePattern":"Invalid version specifier: (.+?)","errorType":"exception","errorClass":"PresetCompatibilityError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/presets/__init__.py","lineNumber":815,"sourceCode":"            PresetCompatibilityError: If pack is incompatible\n        \"\"\"\n        required = manifest.requires_speckit_version\n        # Defense in depth: the manifest validator now rejects a non-string\n        # requires.speckit_version, but this method is public and also reachable\n        # with a hand-built manifest object. ``InvalidSpecifier`` alone does not\n        # cover a non-string -- scalars raise TypeError from the constructor, and\n        # a list/dict is iterable so it constructs here and only breaks inside\n        # .contains(). Reject up front so this always reports a\n        # PresetCompatibilityError.\n        if not isinstance(required, str):\n            raise PresetCompatibilityError(\n                \"Invalid version specifier: expected a string, got \"\n                f\"{type(required).__name__} ({required!r})\"\n            )\n        try:\n            SpecifierSet(required)  # Just to validate\n        except InvalidSpecifier:\n            raise PresetCompatibilityError(f\"Invalid version specifier: {required}\")\n\n        if not version_satisfies(speckit_version, required):\n            raise PresetCompatibilityError(\n                f\"Preset requires spec-kit {required}, \"\n                f\"but {speckit_version} is installed.\\n\"\n                f\"Upgrade spec-kit with: {REINSTALL_COMMAND}\"\n            )\n\n        return True\n\n    def _register_commands(\n        self,\n        manifest: PresetManifest,\n        preset_dir: Path\n    ) -> Dict[str, List[str]]:\n        \"\"\"Register preset command overrides with all detected AI agents.\n\n        Scans the preset's templates for type \"command\", reads each command","sourceCodeStart":797,"sourceCodeEnd":833,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/presets/__init__.py#L797-L833","documentation":"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.","triggerScenarios":"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\".","commonSituations":"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.","solutions":["Use valid PEP 440 specifier syntax: \">=1.0.0\", \"~=1.2\", or comma-separated \">=1.0,<2.0\".","Replace npm-style ^/~ ranges with PEP 440 equivalents (~=x.y.z).","Test your specifier quickly: `python -c \"from packaging.specifiers import SpecifierSet; SpecifierSet('>=1.0')\"`."],"exampleFix":"# before (preset.yml)\nrequires:\n  speckit_version: \"^1.2\"\n\n# after\nrequires:\n  speckit_version: \">=1.2,<2.0\"","handlingStrategy":"validation","validationCode":"from packaging.specifiers import SpecifierSet, InvalidSpecifier\ntry:\n    SpecifierSet(required)\nexcept InvalidSpecifier:\n    raise ValueError(f\"bad specifier {required!r}: use PEP 440 like '>=1.0,<2.0'\")","typeGuard":"from packaging.specifiers import SpecifierSet, InvalidSpecifier\ndef is_valid_specifier(s: str) -> bool:\n    try:\n        SpecifierSet(s)\n        return True\n    except InvalidSpecifier:\n        return False","tryCatchPattern":"try:\n    manager.check_compatibility(manifest, speckit_version)\nexcept PresetCompatibilityError as e:\n    if \"Invalid version specifier\" in str(e):\n        # rewrite requires.speckit_version in valid PEP 440 syntax and retry\n        ...","preventionTips":["Every clause needs an operator: >=, <=, ~=, ==, != — never a bare version.","PEP 440 has no ^ or ~ npm-style ranges; expand them to explicit bounds.","Validate specifiers in CI with packaging.SpecifierSet before publishing a preset."],"tags":["preset","compatibility","version","pep440"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}