unslothai/unsloth · warning · RuntimeError
Linked source changed during reconciliation
Error message
Linked source changed during reconciliation
What it means
Before copying, _snapshot compares the fstat of the opened descriptor against the metadata recorded during _scan (size, mtime, plus device/inode when comparable — see the identity_from_path caveat in the source). A mismatch means the file's content or identity changed between scan and copy, so the snapshot would be an inconsistent mixture and is rejected.
Source
Thrown at studio/backend/core/rag/folder_sync.py:1044
target = ensure_dir(rag_uploads_root()) / f"linked-{uuid.uuid4().hex}{ext}"
before = os.fstat(fd)
if not stat.S_ISREG(before.st_mode):
raise RuntimeError("Linked source is not a regular file")
expected = (
metadata["size_bytes"],
metadata["mtime_ns"],
metadata["device"],
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:View on GitHub (pinned to 203007d190)
Solutions
- Wait for writes to finish and re-run sync; the next cycle reconciles the settled state.
- Sync during quiescent windows (cron at night, post-build) rather than while files stream in.
- Exclude hot directories (logs, downloads-in-progress) from the linked root.
Defensive patterns
Strategy: retry
Validate before calling
import os
def file_is_quiet(metadata: dict) -> bool:
st = os.stat(metadata["path"])
return (st.st_size, st.st_mtime_ns) == (metadata["size_bytes"], metadata["mtime_ns"]) Try / catch
try:
reconcile(folder_id)
except RuntimeError as e:
if "changed during reconciliation" in str(e):
wait_for_writers_to_settle(folder_id)
reconcile(folder_id) # next pass snapshots the settled file
else:
raise Prevention
- Sync linked folders only when writers (apps, downloads, exporters) are idle.
- Exclude actively-written directories (logs, tmp, downloads) from the linked root.
- Batch the heavy writes, then trigger sync explicitly.
When it happens
Trigger: A file is appended to, truncated, touched, or rewritten (new inode) after _scan but before _snapshot processes it — typically during active write workloads on the linked folder (logs being written, editors saving, CI output).
Common situations: Linking a folder that applications actively write to; syncing while a big copy/download into the folder is in progress; editors using atomic replace (new inode) on every save.
Related errors
- Linked folder root identity changed during scan
- Linked source changed while it was copied
- Linked folder changed after it was selected
- Linked folder root identity changed
- Folder nesting exceeds the {_MAX_FOLDER_DEPTH}-level scan li
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/51583337d23ef7f1.
Report an issue: GitHub.