pytest-dev/pytest · error · OSError

The temporary directory

Error message

The temporary directory {rootdir} is not owned by the current user. Fix this and try again.

What it means

Companion to the symlink check: pytest stats the default basetemp root (pytest-of-<user>) and requires its owning uid to match the current process uid. A directory owned by another user could be modified by that user to influence or observe pytest's temp files, so pytest refuses rather than chmod or chown it. The source comment explicitly notes this is also the failure mode for the known mkdir-DoS prank.

Solutions

  1. Delete the directory so pytest recreates it with the current uid: `rm -rf /tmp/pytest-of-<user>`.
  2. Run pytest with --basetemp pointing at a path the current user owns.
  3. Set TMPDIR to a per-user writable location (e.g. under $HOME or a fresh container scratch).
  4. In containers, ensure the runtime user matches the owner of any pre-existing temp tree, or create the temp tree as that user.

Example fix

# before: dir owned by root, tests run as 'app'
# /tmp/pytest-of-app owned by uid 0

sudo rm -rf /tmp/pytest-of-app
# after: pytest recreates it owned by 'app'
# or: export TMPDIR=/tmp/app-$$ && mkdir -p "$TMPDIR"
Defensive patterns

Strategy: validation

Validate before calling

import os, getpass

def ensure_owned_temproot() -> str:
    """Pick a temp root owned by the current uid, or prepare one."""
    uid = os.geteuid()
    root = os.path.join(os.environ.get("TMPDIR", "/tmp"), f"pytest-of-{getpass.getuser()}")
    if os.path.exists(root) and os.stat(root).st_uid != uid:
        # Not ours — point elsewhere rather than fight ownership.
        os.environ["TMPDIR"] = os.path.join("/tmp", f"pytest-of-{getpass.getuser()}-{uid}")
    return os.environ["TMPDIR"]

Type guard

import os

def basetemp_owned_by_current_user(path: str) -> bool:
    if not os.path.exists(path):
        return True
    return os.stat(path).st_uid == os.geteuid()

Try / catch

# OSError fires before tests; wrap the invocation to recover.
import os, shutil, getpass, subprocess, sys
root = os.path.join(os.environ.get("TMPDIR", "/tmp"), f"pytest-of-{getpass.getuser()}")
try:
    subprocess.check_call([sys.executable, "-m", "pytest"])
except subprocess.CalledProcessError:
    if os.path.exists(root) and os.stat(root).st_uid != os.geteuid():
        shutil.rmtree(root, ignore_errors=True)
    raise

Prevention

When it happens

Trigger: Another user (or root) created /tmp/pytest-of-<user> first; tests run as a different uid than the one that created the dir (common in containers that drop privileges); a restored-from-image temp tree owned by a prior user id; the documented prank where someone pre-creates the dir.

Common situations: Container images that run pytest as a non-root user after root created /tmp; CI that reuses a cache volume across jobs with different uids; NFS or shared scratch where ownership differs; sudo'd test runs leaving root-owned dirs.

Related errors


AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11). Data as JSON: /api/errors/4339b8d650adc2d1. Report an issue: GitHub.

Appendix: source

Thrown at src/_pytest/tmpdir.py:194

            # TOCTOU vulnerability.
            # This check makes us vulnerable to a DoS - a user can `mkdir
            # /tmp/pytest-of-otheruser` and then `otheruser` will fail this
            # check. For now we don't consider it a real problem. otheruser can
            # change their TMPDIR or --basetemp, and maybe give the prankster a
            # good scolding.
            uid = get_user_id()
            if uid is not None:
                stat_follow_symlinks = (
                    False if os.stat in os.supports_follow_symlinks else True
                )
                rootdir_stat = rootdir.stat(follow_symlinks=stat_follow_symlinks)
                if stat.S_ISLNK(rootdir_stat.st_mode):
                    raise OSError(
                        f"The temporary directory {rootdir} is a symbolic link. "
                        "Fix this and try again."
                    )
                if rootdir_stat.st_uid != uid:
                    raise OSError(
                        f"The temporary directory {rootdir} is not owned by the current user. "
                        "Fix this and try again."
                    )
                if (rootdir_stat.st_mode & 0o077) != 0:
                    chmod_follow_symlinks = (
                        False if os.chmod in os.supports_follow_symlinks else True
                    )
                    rootdir.chmod(
                        rootdir_stat.st_mode & ~0o077,
                        follow_symlinks=chmod_follow_symlinks,
                    )
            keep = self._retention_count
            if self._retention_policy == "none":
                keep = 0
            basetemp = make_numbered_dir_with_cleanup(
                prefix="pytest-",
                root=rootdir,
                keep=keep,

View on GitHub (pinned to 0d6fbdeffa)