pytest-dev/pytest · error · ValueError
{basename} is not a normalized and relative path
Error message
{basename} is not a normalized and relative path What it means
Raised by TempPathFactory._ensure_relative_to_basetemp when the basename passed to mktemp does not normalize to a path whose parent is the basetemp. This blocks path traversal: a basename containing '..' or an absolute/rooted path would escape the basetemp directory. After normpath, pytest resolves (basetemp/basename).parent and requires it to equal basetemp.
Source
Thrown at src/_pytest/tmpdir.py:114
if count < 0:
raise ValueError(
f"tmp_path_retention_count must be >= 0. Current input: {count}."
)
policy: RetentionType = config.getini("tmp_path_retention_policy")
return cls(
given_basetemp=config.option.basetemp,
trace=config.trace.get("tmpdir"),
retention_count=count,
retention_policy=policy,
_ispytest=True,
)
def _ensure_relative_to_basetemp(self, basename: str) -> str:
basename = os.path.normpath(basename)
if (self.getbasetemp() / basename).resolve().parent != self.getbasetemp():
raise ValueError(f"{basename} is not a normalized and relative path")
return basename
def mktemp(self, basename: str, numbered: bool = True) -> Path:
"""Create a new temporary directory managed by the factory.
:param basename:
Directory base name, must be a relative path.
:param numbered:
If ``True``, ensure the directory is unique by adding a numbered
suffix greater than any existing one: ``basename="foo-"`` and ``numbered=True``
means that this function will create directories named ``"foo-0"``,
``"foo-1"``, ``"foo-2"`` and so on.
:returns:
The path to the new directory.
"""
basename = self._ensure_relative_to_basetemp(basename)View on GitHub (pinned to 98b357f69e)
Solutions
- Pass a simple relative basename (no slashes, no leading '/'); sanitize with re.sub(r'[\\W]+', '_', name) as the built-in _mk_tmp does.
- If you need subdirectories, create them inside the returned Path with Path.mkdir(parents=True) rather than encoding them into the basename.
- Never feed user/test-param input directly into mktemp; strip path separators first.
Example fix
// before
def test_x(tmp_path_factory, request):
d = tmp_path_factory.mktemp(request.param) # request.param may contain '../'
// after
import re
def test_x(tmp_path_factory, request):
safe = re.sub(r'[\\W]+', '_', request.param)[:30]
d = tmp_path_factory.mktemp(safe) Defensive patterns
Strategy: validation
Validate before calling
import os, re
def safe_basename(name: str) -> str:
name = re.sub(r'[^A-Za-z0-9_.-]+', '_', name)[:30]
if os.path.normpath(name) != name or os.path.isabs(name):
raise ValueError(f"unsafe basename: {name!r}")
return name Type guard
import os
def is_safe_basename(name: str) -> bool:
n = os.path.normpath(name)
return n == name and not os.path.isabs(n) and '..' not in n.split(os.sep) Prevention
- Never pass user/test-param strings directly to mktemp; sanitize first.
- Prefer single-segment basenames; build subdirs with Path.mkdir on the result.
- Mirror pytest's own re.sub(r'[\\W]', '_', name)[:30] sanitization.
When it happens
Trigger: Calling tmp_path_factory.mktemp('../evil'), mktemp('/abs/path'), or mktemp('foo/../../bar'). Also reachable via custom fixtures that derive the basename from untrusted or user-supplied input (e.g. parameterized ids containing slashes or dots).
Common situations: A fixture builds the tmp dir name from a test id/param that includes slashes; pytester or integration code reusing mktemp with externally supplied strings; misuse of the internal mktemp API by a plugin.
Related errors
- tmp_path_retention_count must be >= 0. Current input: {count
- The temporary directory {rootdir} is a symbolic link. Fix th
- The temporary directory {rootdir} is not owned by the curren
- name is not allowed to contain path separators
- relative tolerance for a scalar value must be an int, float
AI-assisted analysis of pytest-dev/pytest@98b357f69e (2026-08-04).
Data as JSON: /data/errors/acd042c01c55dc1d.json.
Report an issue: GitHub.