headroomlabs-ai/headroom · error · ValueError
expected one Ruff dev dependency, found {ruff_requirements!r
Error message
expected one Ruff dev dependency, found {ruff_requirements!r} What it means
verify-ruff-version.py reads `project.optional-dependencies.dev` from pyproject.toml and requires exactly one requirement starting with `ruff`. Zero entries (Ruff dependency removed) or two-plus (e.g., both `ruff` and a `ruff-*` plugin pinned) fail, because the script cannot tell which one is the authoritative version pin.
Source
Thrown at scripts/verify-ruff-version.py:33
ROOT = Path(__file__).resolve().parent.parent
RUFF_PRE_COMMIT_REPO = "https://github.com/astral-sh/ruff-pre-commit"
WORKFLOW_VERSION_COMMAND = "python scripts/verify-ruff-version.py --print-version"
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:View on GitHub (pinned to 322425c43b)
Solutions
- Inspect pyproject.toml's dev group: `grep -n ruff pyproject.toml` and keep exactly one entry.
- Remove the duplicate, or rename the non-tool dependency so it no longer starts with `ruff`.
- If Ruff moved groups on purpose, update the script's lookup path to the new group.
Example fix
# pyproject.toml - before [project.optional-dependencies] dev = ["ruff==0.5.0", "ruff-lsp==0.1.0"] # after [project.optional-dependencies] dev = ["ruff==0.5.0"]
Defensive patterns
Strategy: validation
Validate before calling
import tomllib
from pathlib import Path
def ruff_pins(pyproject: Path) -> list[str]:
deps = tomllib.loads(pyproject.read_text())["project"]["optional-dependencies"]["dev"]
return [d for d in deps if d.startswith("ruff")]
# assert len(ruff_pins(...)) == 1 in a pre-commit hook Prevention
- Keep exactly one Ruff entry in the dev group; name any unrelated package so it does not start with 'ruff'.
- Review dependency-bot PRs for duplicated Ruff requirements.
- Run scripts/verify-ruff-version.py in CI so drift is caught at PR time.
When it happens
Trigger: Adding a second Ruff-family dev dependency (any requirement string beginning with `ruff`, such as `ruff-vscode` or a duplicated pin); removing the Ruff pin entirely; moving Ruff to a different dependency group.
Common situations: Dependabot/Renovate PRs that add or duplicate Ruff entries; refactors moving dev deps into separate groups; a rename that accidentally starts with the substring `ruff`.
Related errors
- pyproject.toml must contain one exact Ruff pin in project.op
- expected one locked Ruff package, found {versions!r}
- sentence-transformers is required for LocalEmbedder. Install
- openai is required for OpenAIEmbedder. Install it with: pip
- httpx is required for OllamaEmbedder. Install it with: pip i
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/75b3d76f024c1787.
Report an issue: GitHub.