{"record":{"id":"ae200507b967eafb","repo":"AlexsJones/llmfit","slug":"invalid-version-version-r","errorCode":null,"errorMessage":"Invalid version: {version!r}","messagePattern":"Invalid version: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"llmfit-python/hatch_build.py","lineNumber":74,"sourceCode":"\n    PLUGIN_NAME = \"llmfit version, license and readme\"\n\n    def update(self, metadata: dict) -> None:\n        \"\"\"Populate dynamic metadata from the repository.\n\n        ``version`` and ``license-expression`` come from ``Cargo.toml``,\n        ``readme`` from the repository root.\n\n        Version resolution order:\n\n        1. ``LLMFIT_VERSION`` environment variable (e.g. ``0.9.8``).\n        2. The ``version`` field in ``[workspace.package]`` from ``Cargo.toml``.\n        \"\"\"\n        with (Path(self.root).parent / \"Cargo.toml\").open(\"rb\") as f:\n            workspace_package: dict[str, str] = tomli.load(f)[\"workspace\"][\"package\"]\n        version: str = os.environ.get(\"LLMFIT_VERSION\") or workspace_package[\"version\"]\n        if not re.match(r\"^\\d+\\.\\d+\\.\\d+$\", version):\n            raise ValueError(f\"Invalid version: {version!r}\")\n        metadata[\"version\"] = version\n        metadata[\"license-expression\"] = workspace_package[\"license\"]\n\n        # The package README is the repository one. Hatchling refuses a\n        # `readme = \"../README.md\"` path (\"must be within the project\n        # directory\") but takes the contents verbatim, so read it here rather\n        # than duplicating or symlinking the file.\n        readme = Path(self.root).parent / \"README.md\"\n        metadata[\"readme\"] = {\n            \"content-type\": \"text/markdown\",\n            \"text\": readme.read_text(encoding=\"utf-8\"),\n        }\n\n\nclass LlmfitBinaryBuildHook(BuildHookInterface):\n    \"\"\"Hatchling build hook that injects the llmfit binary into each wheel.\"\"\"\n\n    PLUGIN_NAME = \"llmfit binary\"","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/AlexsJones/llmfit/blob/acc7e40c3a0afbd36510a92f2f8f3d5177cfc0fe/llmfit-python/hatch_build.py#L56-L92","documentation":"Raised by the LlmfitMetadataHook in hatch_build.py while Hatchling resolves package metadata. The version is taken from the LLMFIT_VERSION environment variable or the [workspace.package] version field of the root Cargo.toml, and it must match the strict regex ^\\d+\\.\\d+\\.\\d+$. Any deviation from bare X.Y.Z — pre-release suffix, build metadata, or a leading 'v' — is rejected with this ValueError before the wheel/sdist metadata is set.","triggerScenarios":"Running `uv build`, `hatch build`, or `pip install -e llmfit-python` when: (1) LLMFIT_VERSION is exported as something like '0.9.8-rc1', 'v0.9.8', or '0.10'; (2) Cargo.toml's [workspace.package] version was bumped to a pre-release such as '1.0.0-beta.1'; (3) CI writes a git tag (with a 'v' prefix or suffix) into LLMFIT_VERSION verbatim.","commonSituations":"Cutting a release candidate, tagging with 'v' prefixes, or a CI job that derives LLMFIT_VERSION from `git describe` output. Also happens after manually editing Cargo.toml and leaving stray characters in the version string.","solutions":["Set [workspace.package].version in Cargo.toml to a plain 'X.Y.Z' string (e.g. '0.9.8') with no prefix or suffix","Check the environment: `echo $LLMFIT_VERSION` — if it carries a prefix/suffix, export a clean value or `unset LLMFIT_VERSION` so Cargo.toml is used","If pre-release versions must be supported, relax the guard in hatch_build.py to r\"^\\d+\\.\\d+\\.\\d+(?:[-+][0-9A-Za-z.-]+)?$\" and normalize to PEP 440 before assigning metadata['version']"],"exampleFix":"# before (hatch_build.py)\nif not re.match(r\"^\\d+\\.\\d+\\.\\d+$\", version):\n    raise ValueError(f\"Invalid version: {version!r}\")\n\n# after — allow a pre-release/build suffix\nif not re.match(r\"^\\d+\\.\\d+\\.\\d+(?:[-+][0-9A-Za-z.-]+)?$\", version):\n    raise ValueError(f\"Invalid version: {version!r}\")","handlingStrategy":"validation","validationCode":"import os, re, tomllib\nversion = os.environ.get(\"LLMFIT_VERSION\") or tomllib.load(open(\"Cargo.toml\", \"rb\"))[\"workspace\"][\"package\"][\"version\"]\nif not re.fullmatch(r\"\\d+\\.\\d+\\.\\d+\", version):\n    raise SystemExit(f\"LLMFIT_VERSION/Cargo.toml version {version!r} is not plain X.Y.Z — fix before building\")","typeGuard":null,"tryCatchPattern":"try:\n    subprocess.run([\"uv\", \"build\"], check=True)\nexcept subprocess.CalledProcessError as e:\n    # hatchling surfaces the hook's ValueError in stderr; fail the release job early\n    raise SystemExit(f\"wheel build failed (check version string): {e}\")","preventionTips":["Keep [workspace.package].version in Cargo.toml as bare semver; never commit pre-release strings","In CI, validate LLMFIT_VERSION with a regex step before invoking the build","Derive LLMFIT_VERSION from a tag with 'v' stripped and suffixes removed"],"tags":["python","hatch","packaging","version","build-hook"],"backgroundTag":"invalid-version-string","analyzedSha":"acc7e40c3a0afbd36510a92f2f8f3d5177cfc0fe","analyzedAt":"2026-08-17T10:35:29.658Z","contentChangedAt":"2026-08-17T10:35:29.658Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}