{"record":{"id":"64e5b38217ee42a1","repo":"pytest-dev/pytest","slug":"lock-path-got-renamed-after-successful-creation","errorCode":null,"errorMessage":"lock path got renamed after successful creation","messagePattern":"lock path got renamed after successful creation","errorType":"exception","errorClass":"OSError","httpStatus":null,"severity":"error","filePath":"src/_pytest/pathlib.py","lineNumber":302,"sourceCode":"            \"could not create numbered dir with prefix \"\n            f\"{prefix} in {root} after 10 tries\"\n        )\n\n\ndef create_cleanup_lock(p: Path) -> Path:\n    \"\"\"Create a lock to prevent premature directory cleanup.\"\"\"\n    lock_path = get_lock_path(p)\n    try:\n        fd = os.open(str(lock_path), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644)\n    except FileExistsError as e:\n        raise OSError(f\"cannot create lockfile in {p}\") from e\n    else:\n        pid = os.getpid()\n        spid = str(pid).encode()\n        os.write(fd, spid)\n        os.close(fd)\n        if not lock_path.is_file():\n            raise OSError(\"lock path got renamed after successful creation\")\n        return lock_path\n\n\ndef register_cleanup_lock_removal(lock_path: Path, register: Any) -> Any:\n    \"\"\"Register a cleanup function for removing a lock.\"\"\"\n    pid = os.getpid()\n\n    def cleanup_on_exit(lock_path: Path = lock_path, original_pid: int = pid) -> None:\n        current_pid = os.getpid()\n        if current_pid != original_pid:\n            # fork\n            return\n        try:\n            lock_path.unlink()\n        except OSError:\n            pass\n\n    return register(cleanup_on_exit)","sourceCodeStart":284,"sourceCodeEnd":320,"githubUrl":"https://github.com/pytest-dev/pytest/blob/0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc/src/_pytest/pathlib.py#L284-L320","documentation":"Raised by create_cleanup_lock after a lock file is successfully created and written, but is_found.is_file() returns False immediately after (lines 297-302). This detects a TOCTOU race where another process moved or removed the lock between creation and the existence check. It signals filesystem instability or concurrent cleanup interference.","triggerScenarios":"A concurrent cleanup process deletes or renames the lock right after creation; antivirus or sync software (Dropbox, OneDrive) moving newly created files; network filesystems with delayed visibility.","commonSituations":"Cloud-synced tmp directories; NFS with caching; concurrent pytest runs with overlapping cleanup windows.","solutions":["Point pytest's basetemp at a local, non-synced filesystem.","Avoid concurrent pytest runs sharing the same tmp root.","Exclude the tmp dir from sync/antivirus scanning."],"exampleFix":"// before\npytest --basetemp=~/Dropbox/pytest\n// after\npytest --basetemp=/tmp/pytest-run","handlingStrategy":"fallback","validationCode":"import os\nif any(sync in str(p) for sync in ['Dropbox', 'OneDrive', 'icloud']):\n    raise RuntimeError('basetemp on a synced volume is unsafe')","typeGuard":"def _on_local_fs(p) -> bool:\n    import os\n    return not any(s in str(p) for s in ('Dropbox', 'OneDrive', 'icloud'))","tryCatchPattern":null,"preventionTips":["Keep pytest basetemp off cloud-synced or network volumes.","Avoid overlapping concurrent cleanup runs."],"tags":["pytest","pathlib","oserror","race-condition","filesystem"],"backgroundTag":null,"analyzedSha":"0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc","analyzedAt":"2026-08-11T20:52:36.969Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}