unslothai/unsloth · warning · RuntimeError

Linked source changed while it was copied

Error message

Linked source changed while it was copied

What it means

After _copy_exact finishes, _snapshot fstats the still-open source descriptor again and compares (size, mtime_ns, dev, ino) to the pre-copy values. Any drift means the file was modified while it was being read, so the completed snapshot could be torn and is deleted via _remove_snapshot before the error propagates.

Source

Thrown at studio/backend/core/rag/folder_sync.py:1052

            metadata["inode"],
        )
        actual = (before.st_size, before.st_mtime_ns, before.st_dev, before.st_ino)
        # Only an identity os.fstat can reproduce is comparable here: os.scandir reports none at all
        # on Windows, and the os.lstat _scan falls back to disagrees with os.fstat on file systems
        # without stable file ids. Size and mtime still gate those, and the post-copy check below is
        # fstat to fstat either way.
        usable = metadata["inode"] not in (None, 0) and not metadata.get("identity_from_path")
        compared = 4 if usable else 2
        if actual[:compared] != expected[:compared]:
            raise RuntimeError("Linked source changed during reconciliation")
        if config.MAX_UPLOAD_BYTES and before.st_size > config.MAX_UPLOAD_BYTES:
            raise RuntimeError("Linked source exceeds the RAG file size limit")
        with os.fdopen(fd, "rb", closefd = False) as src, open(target, "xb") as dst:
            _copy_exact(src, dst, before.st_size)
        after = os.fstat(fd)
        # Both sides are fstat here, so the identity is always comparable.
        if (after.st_size, after.st_mtime_ns, after.st_dev, after.st_ino) != actual:
            raise RuntimeError("Linked source changed while it was copied")
        return str(target)
    except Exception:
        if target is not None:
            _remove_snapshot(str(target))
        raise
    finally:
        os.close(fd)


def _copy_exact(source, target, size: int) -> None:
    remaining = size
    while remaining:
        block = source.read(min(1 << 20, remaining))
        if not block:
            raise RuntimeError("Linked source changed while it was copied")
        target.write(block)
        remaining -= len(block)
    if source.read(1):

View on GitHub (pinned to 203007d190)

Solutions

  1. Quiesce writers on the linked folder and re-run sync — the next pass snapshots the settled file.
  2. Sync on a schedule when applications are idle; exclude actively-written directories from the link.
  3. For log-like data, snapshot from rotated (closed) files rather than the live one.
Defensive patterns

Strategy: retry

Validate before calling

import os

def file_still_settled(path: str, size: int, mtime_ns: int) -> bool:
    st = os.stat(path)
    return st.st_size == size and st.st_mtime_ns == mtime_ns

Try / catch

try:
    reconcile(folder_id)
except RuntimeError as e:
    if "changed while it was copied" in str(e):
        wait_and_resync(folder_id)  # snapshot of the settled file lands next cycle
    else:
        raise

Prevention

When it happens

Trigger: The source file is written, truncated, or atomically replaced (new inode) during the copy loop itself; large files make the copy window long enough for concurrent writers (logs, exporters, editors) to interleave.

Common situations: Syncing directories that another process actively writes (database export dirs, download folders, live log trees); slow network shares stretching copy time into write windows.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/2a5046f7d03c6d61. Report an issue: GitHub.