headroomlabs-ai/headroom · error · ValueError

pyproject.toml must contain one exact Ruff pin in project.op

Error message

pyproject.toml must contain one exact Ruff pin in project.optional-dependencies.dev

What it means

After finding the single Ruff dev dependency, verify-ruff-version.py requires it to be an exact pin matching `ruff==<version>` exactly (fullmatch, no range, no extras, no environment markers). Loose constraints like `ruff>=0.4` or `ruff` break the invariant that pyproject.toml is the single source of truth for the Ruff version.

Source

Thrown at scripts/verify-ruff-version.py:37

WORKFLOW_INSTALL_REFERENCE = "steps.ruff-version.outputs.version"


def _load_toml(path: Path) -> dict[str, Any]:
    with path.open("rb") as file:
        return cast(dict[str, Any], tomllib.load(file))


def _authoritative_version() -> str:
    dependencies = _load_toml(ROOT / "pyproject.toml")["project"]["optional-dependencies"]["dev"]
    ruff_requirements = [
        requirement for requirement in dependencies if requirement.startswith("ruff")
    ]
    if len(ruff_requirements) != 1:
        raise ValueError(f"expected one Ruff dev dependency, found {ruff_requirements!r}")

    match = re.fullmatch(r"ruff==([^;,\s]+)", ruff_requirements[0])
    if match is None:
        raise ValueError(
            "pyproject.toml must contain one exact Ruff pin in project.optional-dependencies.dev"
        )
    return match.group(1)


def _locked_version() -> str:
    packages = _load_toml(ROOT / "uv.lock")["package"]
    versions = [str(package["version"]) for package in packages if package["name"] == "ruff"]
    if len(versions) != 1:
        raise ValueError(f"expected one locked Ruff package, found {versions!r}")
    return versions[0]


def _pre_commit_version() -> str:
    lines = (ROOT / ".pre-commit-config.yaml").read_text(encoding="utf-8").splitlines()
    for index, line in enumerate(lines):
        if line.strip() != f"- repo: {RUFF_PRE_COMMIT_REPO}":
            continue

View on GitHub (pinned to 322425c43b)

Solutions

  1. Restore the exact pin form: `ruff==X.Y.Z` with no spaces, markers, or extras.
  2. To upgrade Ruff, change the version on the right of `==` (and let the script verify uv.lock and pre-commit agree).
  3. Check for stray characters — the fullmatch rejects `ruff == 0.5.0` and `ruff==0.5 ; python_version>='3.9'` alike.

Example fix

# pyproject.toml - before
[project.optional-dependencies]
dev = ["ruff>=0.5.0"]

# after
[project.optional-dependencies]
dev = ["ruff==0.5.0"]
Defensive patterns

Strategy: validation

Validate before calling

import re

def is_exact_ruff_pin(requirement: str) -> bool:
    return re.fullmatch(r"ruff==([^;,\s]+)", requirement) is not None

Prevention

When it happens

Trigger: Changing the pin to `>=`, `~=`, `!=`, or adding a marker/extras to the requirement string; a dependency bot relaxing the pin; hand-merging a conflict resolution that drops the `==`.

Common situations: Renovate/Dependabot range updates replacing `==` with `>=`; developers loosening the pin to test a pre-release; merge conflicts in pyproject.toml.

Related errors


AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15). Data as JSON: /api/errors/f0e750be33fe7593. Report an issue: GitHub.