astral-sh/uv · error · RuntimeError

{uv_bin_name} was not properly installed

Error message

{uv_bin_name} was not properly installed

What it means

The `uv_build` PEP 517/660 backend is a thin Python shim: every hook (build_sdist, build_wheel, build_editable, ...) calls `call()`, which shells out to the `uv-build` executable (or `uv build-backend` when the distro sets USE_UV_EXECUTABLE=True). PEP 517 frontends run hooks in an isolated subprocess, so the shim cannot use `find_uv_bin`; it relies on `shutil.which(uv_bin_name)`. If that returns None the shim raises RuntimeError to fail fast instead of producing a confusing FileNotFoundError about a missing command.

Source

Thrown at crates/uv-build/python/uv_build/__init__.py:51

    if config_settings:
        print("Warning: Config settings are not supported", file=sys.stderr)


def call(
    args: "Sequence[str]", config_settings: "Mapping[Any, Any] | None" = None
) -> str:
    """Invoke a uv subprocess and return the filename from stdout."""
    import shutil
    import subprocess
    import sys

    warn_config_settings(config_settings)

    uv_bin_name = "uv" if USE_UV_EXECUTABLE else "uv-build"
    # Unlike `find_uv_bin`, this mechanism must work according to PEP 517
    uv_bin = shutil.which(uv_bin_name)
    if uv_bin is None:
        raise RuntimeError(f"{uv_bin_name} was not properly installed")
    build_backend_args = ["build-backend"] if USE_UV_EXECUTABLE else []
    # Forward stderr, capture stdout for the filename
    result = subprocess.run(
        [uv_bin, *build_backend_args, *args], stdout=subprocess.PIPE, check=False
    )
    if result.returncode != 0:
        sys.exit(result.returncode)
    # If there was extra stdout, forward it (there should not be extra stdout)
    stdout = result.stdout.decode("utf-8").strip().splitlines(keepends=True)
    sys.stdout.writelines(stdout[:-1])
    # Fail explicitly instead of an irrelevant stacktrace
    if not stdout:
        print(
            f"{uv_bin_name} subprocess did not return a filename on stdout",
            file=sys.stderr,
        )
        sys.exit(1)
    return stdout[-1].strip()

View on GitHub (pinned to f1a42680ff)

Solutions

  1. In the same environment run `python -c "import shutil; print(shutil.which('uv-build'))"` (or 'uv') to confirm the lookup fails and see which binary name is expected.
  2. Install uv properly so the executable lands in the interpreter's scripts dir: `python -m pip install uv-build` (or the official installer), then prepend that scripts dir to PATH.
  3. If build isolation strips PATH, install the backend in the outer env and build with `--no-build-isolation` (`pip install --no-build-isolation -e .`).
  4. For downstream distributions: keep `uv` on PATH if you build with USE_UV_EXECUTABLE=True, otherwise ship the `uv-build` executable alongside the package.

Example fix

# before: build isolation PATH lacks the backend binary
$ pip install ./pkg
RuntimeError: uv-build was not properly installed

# after: make the executable resolvable, then rebuild
$ python -m pip install uv-build
$ python -c "import shutil; print(shutil.which('uv-build'))"  # prints a path
$ pip install ./pkg
Defensive patterns

Strategy: validation

Validate before calling

import shutil, sysconfig

scripts_dir = sysconfig.get_path("scripts")
expected = "uv" if False else "uv-build"  # match USE_UV_EXECUTABLE for your distro
if shutil.which(expected) is None:
    raise SystemExit(
        f"{expected} not on PATH; install uv/uv-build and add {scripts_dir} to PATH before building"
    )

Try / catch

# PEP 517 frontend / wrapper around the backend hooks
try:
    subprocess.run([sys.executable, "-m", "pip", "wheel", ".", "--no-deps"], check=True)
except subprocess.CalledProcessError as exc:
    if "was not properly installed" in (exc.stderr or ""):
        # binary missing in build env: install it or disable build isolation
        subprocess.run([sys.executable, "-m", "pip", "install", "uv-build"], check=True)
        subprocess.run([sys.executable, "-m", "pip", "wheel", ".", "--no-deps", "--no-build-isolation"], check=True)
    else:
        raise

Prevention

When it happens

Trigger: Any build of a project with `[build-system] build-backend = "uv_build"` where `shutil.which("uv-build")` (or `shutil.which("uv")` for downstream distributions that flip USE_UV_EXECUTABLE) is None in the build environment: `pip install ./pkg`, `pip wheel`, `python -m build`, `uv build` with build isolation, and the hook then runs in an env whose PATH lacks the directory containing the uv-build script.

Common situations: PATH not including the interpreter's scripts/bin directory inside build isolation; installing with `pip install --target` or `--prefix` so the executable lands outside any PATH dir; a venv created without scripts on PATH; downstream packaging (Debian/Nix) that rebuilt `uv_build` with USE_UV_EXECUTABLE=True but did not ship `uv` on PATH; CI images that sanitize PATH.

Related errors


AI-assisted analysis of astral-sh/uv@f1a42680ff (2026-08-16). Data as JSON: /api/errors/185af9d958c50650. Report an issue: GitHub.