AlexsJones/llmfit · error · ValueError

Invalid version

Error message

Invalid version: {version!r}

What it means

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.

Solutions

  1. Set [workspace.package].version in Cargo.toml to a plain 'X.Y.Z' string (e.g. '0.9.8') with no prefix or suffix
  2. 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
  3. 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']

Example fix

# before (hatch_build.py)
if not re.match(r"^\d+\.\d+\.\d+$", version):
    raise ValueError(f"Invalid version: {version!r}")

# after — allow a pre-release/build suffix
if not re.match(r"^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$", version):
    raise ValueError(f"Invalid version: {version!r}")
Defensive patterns

Strategy: validation

Validate before calling

import os, re, tomllib
version = os.environ.get("LLMFIT_VERSION") or tomllib.load(open("Cargo.toml", "rb"))["workspace"]["package"]["version"]
if not re.fullmatch(r"\d+\.\d+\.\d+", version):
    raise SystemExit(f"LLMFIT_VERSION/Cargo.toml version {version!r} is not plain X.Y.Z — fix before building")

Try / catch

try:
    subprocess.run(["uv", "build"], check=True)
except subprocess.CalledProcessError as e:
    # hatchling surfaces the hook's ValueError in stderr; fail the release job early
    raise SystemExit(f"wheel build failed (check version string): {e}")

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of AlexsJones/llmfit@acc7e40c3a (2026-08-17). Data as JSON: /api/errors/ae200507b967eafb. Report an issue: GitHub.

Appendix: source

Thrown at llmfit-python/hatch_build.py:74

    PLUGIN_NAME = "llmfit version, license and readme"

    def update(self, metadata: dict) -> None:
        """Populate dynamic metadata from the repository.

        ``version`` and ``license-expression`` come from ``Cargo.toml``,
        ``readme`` from the repository root.

        Version resolution order:

        1. ``LLMFIT_VERSION`` environment variable (e.g. ``0.9.8``).
        2. The ``version`` field in ``[workspace.package]`` from ``Cargo.toml``.
        """
        with (Path(self.root).parent / "Cargo.toml").open("rb") as f:
            workspace_package: dict[str, str] = tomli.load(f)["workspace"]["package"]
        version: str = os.environ.get("LLMFIT_VERSION") or workspace_package["version"]
        if not re.match(r"^\d+\.\d+\.\d+$", version):
            raise ValueError(f"Invalid version: {version!r}")
        metadata["version"] = version
        metadata["license-expression"] = workspace_package["license"]

        # The package README is the repository one. Hatchling refuses a
        # `readme = "../README.md"` path ("must be within the project
        # directory") but takes the contents verbatim, so read it here rather
        # than duplicating or symlinking the file.
        readme = Path(self.root).parent / "README.md"
        metadata["readme"] = {
            "content-type": "text/markdown",
            "text": readme.read_text(encoding="utf-8"),
        }


class LlmfitBinaryBuildHook(BuildHookInterface):
    """Hatchling build hook that injects the llmfit binary into each wheel."""

    PLUGIN_NAME = "llmfit binary"

View on GitHub (pinned to acc7e40c3a)