pypa/pip · error · ValueError

paths must be relative

Error message

paths must be relative

What it means

pyproject_hooks' norm_and_check raises ValueError('paths must be relative') when a backend-path entry in pyproject.toml [build-system] is an absolute path. PEP 517 requires backend-path entries to be relative so the backend is loaded from within the project source tree.

Source

Thrown at src/pip/_vendor/pyproject_hooks/_impl.py:111

    This uses :func:`subprocess.check_output` under the hood.
    """
    env = os.environ.copy()
    if extra_environ:
        env.update(extra_environ)

    check_output(cmd, cwd=cwd, env=env, stderr=STDOUT)


def norm_and_check(source_tree: str, requested: str) -> str:
    """Normalise and check a backend path.

    Ensure that the requested backend path is specified as a relative path,
    and resolves to a location under the given source tree.

    Return an absolute version of the requested path.
    """
    if os.path.isabs(requested):
        raise ValueError("paths must be relative")

    abs_source = os.path.abspath(source_tree)
    abs_requested = os.path.normpath(os.path.join(abs_source, requested))
    # We have to use commonprefix for Python 2.7 compatibility. So we
    # normalise case to avoid problems because commonprefix is a character
    # based comparison :-(
    norm_source = os.path.normcase(abs_source)
    norm_requested = os.path.normcase(abs_requested)
    if os.path.commonprefix([norm_source, norm_requested]) != norm_source:
        raise ValueError("paths must be inside source tree")

    return abs_requested


class BuildBackendHookCaller:
    """A wrapper to call the build backend hooks for a source directory."""

    def __init__(

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Change the backend-path entry in pyproject.toml to a path relative to the project root (where pyproject.toml lives).
  2. If a tool is absolutizing the path, pass the source_dir correctly so a relative entry resolves properly.
  3. Move the backend code under the source tree and reference it relatively.

Example fix

# before
[build-system]
build-backend = "my_backend"
backend-path = ["/home/user/proj/my_backend"]

# after
[build-system]
build-backend = "my_backend"
backend-path = ["my_backend"]
Defensive patterns

Strategy: validation

Validate before calling

import os
for p in backend_paths:
    if os.path.isabs(p):
        raise ValueError(f'backend-path must be relative: {p!r}')

Type guard

def is_relative_path(p: str) -> bool:
    return isinstance(p, str) and not os.path.isabs(p)

Try / catch

from pip._vendor.pyproject_hooks._impl import BuildBackendHookCaller
try:
    caller = BuildBackendHookCaller(src, backend, backend_path=paths)
except ValueError:
    paths = [os.path.relpath(p, src) for p in paths]
    caller = BuildBackendHookCaller(src, backend, backend_path=paths)

Prevention

When it happens

Trigger: Constructing BuildBackendHookCaller with backend_path containing an absolute path (os.path.isabs(requested) is True), which happens when pyproject.toml lists absolute directories under [build-system] backend-path, e.g. backend-path = ['/abs/repo/backend'].

Common situations: Hand-editing pyproject.toml to point at an absolute backend location; build tools that resolve paths to absolute before passing them; Windows paths like 'C:\\backends\\x' that are inherently absolute.

Related errors


AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04). Data as JSON: /data/errors/1bbf792074655909.json. Report an issue: GitHub.