pypa/pip · error · ValueError
paths must be inside source tree
Error message
paths must be inside source tree
What it means
pyproject_hooks' norm_and_check raises ValueError('paths must be inside source tree') when a relative backend-path entry, after normalization, escapes the source tree. This guards against directory traversal: an entry like '../sibling' would resolve outside the project and is rejected.
Source
Thrown at src/pip/_vendor/pyproject_hooks/_impl.py:121
"""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__(
self,
source_dir: str,
build_backend: str,
backend_path: Optional[Sequence[str]] = None,
runner: Optional["SubprocessRunner"] = None,
python_executable: Optional[str] = None,
) -> None:
"""
:param source_dir: The source directory to invoke the build backend for
:param build_backend: The build backend specView on GitHub (pinned to d7d0d0a394)
Solutions
- Move the backend code inside the source tree and use a path with no '..' components.
- Remove the offending traversal entry from backend-path in pyproject.toml.
- Ensure source_dir passed to BuildBackendHookCaller is the project root the backend lives under.
Example fix
# before [build-system] backend-path = ["../shared_backend"] # after [build-system] backend-path = ["shared_backend"] # after moving it inside the project
Defensive patterns
Strategy: validation
Validate before calling
import os
abs_src = os.path.abspath(source_tree)
for p in backend_paths:
abs_p = os.path.normpath(os.path.join(abs_src, p))
if os.path.commonprefix([os.path.normcase(abs_src), os.path.normcase(abs_p)]) != os.path.normcase(abs_src):
raise ValueError(f'backend-path escapes source tree: {p!r}') Type guard
def is_inside_tree(source_tree: str, p: str) -> bool:
import os
a = os.path.normcase(os.path.normpath(os.path.join(os.path.abspath(source_tree), p)))
s = os.path.normcase(os.path.abspath(source_tree))
return a == s or a.startswith(s + os.sep) Try / catch
try:
caller = BuildBackendHookCaller(src, backend, backend_path=paths)
except ValueError:
paths = [p for p in paths if not p.startswith('..')]
caller = BuildBackendHookCaller(src, backend, backend_path=paths) Prevention
- Avoid '..' components in backend-path.
- Keep backend code under the project root.
When it happens
Trigger: Constructing BuildBackendHookCaller with backend_path containing a traversal such as '../backend' or subpaths that, once joined and normcased, no longer share the source tree prefix (os.path.commonprefix check fails).
Common situations: Backend code living outside the project directory referenced via '../'; symlinks or case-insensitive path mismatches on macOS/Windows; refactoring that moved the backend without updating pyproject.toml.
Related errors
- paths must be relative
- Cannot find module {self.backend_module!r} in {self.backend_
- Error while importing backend
- Cannot import {mod_path!r}
- Some build dependencies for {requirement} conflict with {con
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/759e5355523c6914.json.
Report an issue: GitHub.