pytest-dev/pytest · error · OSError

lock path got renamed after successful creation

Error message

lock path got renamed after successful creation

What it means

After successfully creating the cleanup lock file, pytest re-checks it still exists. If another process renamed or removed it in the window between creation and verification, it raises OSError as a race-condition guard, signalling the lock is no longer authoritative.

Source

Thrown at src/_pytest/pathlib.py:302

            "could not create numbered dir with prefix "
            f"{prefix} in {root} after 10 tries"
        )


def create_cleanup_lock(p: Path) -> Path:
    """Create a lock to prevent premature directory cleanup."""
    lock_path = get_lock_path(p)
    try:
        fd = os.open(str(lock_path), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644)
    except FileExistsError as e:
        raise OSError(f"cannot create lockfile in {p}") from e
    else:
        pid = os.getpid()
        spid = str(pid).encode()
        os.write(fd, spid)
        os.close(fd)
        if not lock_path.is_file():
            raise OSError("lock path got renamed after successful creation")
        return lock_path


def register_cleanup_lock_removal(lock_path: Path, register: Any) -> Any:
    """Register a cleanup function for removing a lock."""
    pid = os.getpid()

    def cleanup_on_exit(lock_path: Path = lock_path, original_pid: int = pid) -> None:
        current_pid = os.getpid()
        if current_pid != original_pid:
            # fork
            return
        try:
            lock_path.unlink()
        except OSError:
            pass

    return register(cleanup_on_exit)

View on GitHub (pinned to 98b357f69e)

Solutions

  1. Run only one cache-cleaner / pytest per cache directory at a time
  2. Give each worker its own --cachedir / TMPDIR to remove contention
  3. Retry the pytest invocation once the concurrent cleanup has stopped
Defensive patterns

Strategy: retry

Try / catch

import time
for attempt in range(3):
    try:
        create_cleanup_lock(p)
        break
    except OSError:
        time.sleep(0.1)
else:
    raise

Prevention

When it happens

Trigger: Another concurrent process deletes/renames the freshly-created .lock file before the is_file() re-check at pathlib.py:301 completes. Essentially a TOCTOU race on the lock path.

Common situations: Multiple cache-cleaner or pytest processes contending over the same directory; external cleanup (cron, janitor) touching the cache dir mid-run.

Related errors


AI-assisted analysis of pytest-dev/pytest@98b357f69e (2026-08-04). Data as JSON: /data/errors/64e5b38217ee42a1.json. Report an issue: GitHub.