{"record":{"id":"4339b8d650adc2d1","repo":"pytest-dev/pytest","slug":"the-temporary-directory-rootdir-is-not-owned-by","errorCode":null,"errorMessage":"The temporary directory {rootdir} is not owned by the current user. Fix this and try again.","messagePattern":"The temporary directory (.+?) is not owned by the current user\\. Fix this and try again\\.","errorType":"exception","errorClass":"OSError","httpStatus":null,"severity":"error","filePath":"src/_pytest/tmpdir.py","lineNumber":194,"sourceCode":"            # TOCTOU vulnerability.\n            # This check makes us vulnerable to a DoS - a user can `mkdir\n            # /tmp/pytest-of-otheruser` and then `otheruser` will fail this\n            # check. For now we don't consider it a real problem. otheruser can\n            # change their TMPDIR or --basetemp, and maybe give the prankster a\n            # good scolding.\n            uid = get_user_id()\n            if uid is not None:\n                stat_follow_symlinks = (\n                    False if os.stat in os.supports_follow_symlinks else True\n                )\n                rootdir_stat = rootdir.stat(follow_symlinks=stat_follow_symlinks)\n                if stat.S_ISLNK(rootdir_stat.st_mode):\n                    raise OSError(\n                        f\"The temporary directory {rootdir} is a symbolic link. \"\n                        \"Fix this and try again.\"\n                    )\n                if rootdir_stat.st_uid != uid:\n                    raise OSError(\n                        f\"The temporary directory {rootdir} is not owned by the current user. \"\n                        \"Fix this and try again.\"\n                    )\n                if (rootdir_stat.st_mode & 0o077) != 0:\n                    chmod_follow_symlinks = (\n                        False if os.chmod in os.supports_follow_symlinks else True\n                    )\n                    rootdir.chmod(\n                        rootdir_stat.st_mode & ~0o077,\n                        follow_symlinks=chmod_follow_symlinks,\n                    )\n            keep = self._retention_count\n            if self._retention_policy == \"none\":\n                keep = 0\n            basetemp = make_numbered_dir_with_cleanup(\n                prefix=\"pytest-\",\n                root=rootdir,\n                keep=keep,","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/pytest-dev/pytest/blob/0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc/src/_pytest/tmpdir.py#L176-L212","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Delete the directory so pytest recreates it with the current uid: `rm -rf /tmp/pytest-of-<user>`.","Run pytest with --basetemp pointing at a path the current user owns.","Set TMPDIR to a per-user writable location (e.g. under $HOME or a fresh container scratch).","In containers, ensure the runtime user matches the owner of any pre-existing temp tree, or create the temp tree as that user."],"exampleFix":"# before: dir owned by root, tests run as 'app'\n# /tmp/pytest-of-app owned by uid 0\n\nsudo rm -rf /tmp/pytest-of-app\n# after: pytest recreates it owned by 'app'\n# or: export TMPDIR=/tmp/app-$$ && mkdir -p \"$TMPDIR\"","handlingStrategy":"validation","validationCode":"import os, getpass\n\ndef ensure_owned_temproot() -> str:\n    \"\"\"Pick a temp root owned by the current uid, or prepare one.\"\"\"\n    uid = os.geteuid()\n    root = os.path.join(os.environ.get(\"TMPDIR\", \"/tmp\"), f\"pytest-of-{getpass.getuser()}\")\n    if os.path.exists(root) and os.stat(root).st_uid != uid:\n        # Not ours — point elsewhere rather than fight ownership.\n        os.environ[\"TMPDIR\"] = os.path.join(\"/tmp\", f\"pytest-of-{getpass.getuser()}-{uid}\")\n    return os.environ[\"TMPDIR\"]","typeGuard":"import os\n\ndef basetemp_owned_by_current_user(path: str) -> bool:\n    if not os.path.exists(path):\n        return True\n    return os.stat(path).st_uid == os.geteuid()","tryCatchPattern":"# OSError fires before tests; wrap the invocation to recover.\nimport os, shutil, getpass, subprocess, sys\nroot = os.path.join(os.environ.get(\"TMPDIR\", \"/tmp\"), f\"pytest-of-{getpass.getuser()}\")\ntry:\n    subprocess.check_call([sys.executable, \"-m\", \"pytest\"])\nexcept subprocess.CalledProcessError:\n    if os.path.exists(root) and os.stat(root).st_uid != os.geteuid():\n        shutil.rmtree(root, ignore_errors=True)\n    raise","preventionTips":["Run pytest as the same uid that owns (or will own) the temp tree.","In containers, create /tmp/pytest-of-<user> as the runtime user, not root.","Clean the temp tree in CI between jobs so stale ownership does not leak."],"tags":["security","tmp-path","ownership","filesystem","containers"],"backgroundTag":null,"analyzedSha":"0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc","analyzedAt":"2026-08-11T20:52:36.969Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}