unslothai/unsloth · error · RuntimeError

Linked folder root identity changed

Error message

Linked folder root identity changed

What it means

_scan() re-derives the folder's root identity and compares it to expected_identity (the st_dev/st_ino persisted in linked_folders). A mismatch means the physical directory at the registered path changed identity — replaced, moved, or remounted — so the scan aborts before walking files, preventing ingestion under a swapped root.

Source

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

            yield row
            previous = state
            last_sent = time.monotonic()
        elif time.monotonic() - last_sent >= _JOB_EVENT_KEEPALIVE_S:
            yield None
            last_sent = time.monotonic()
        if row["status"] in _TERMINAL:
            return
        time.sleep(0.5)


def _scan(
    root: str, expected_identity: tuple[int, int] | None = None
) -> tuple[dict[str, dict], tuple[int, int]]:
    from hub.storage.scan_folders import contains_sensitive_path_component, is_denied_system_path

    identity = _root_identity(root)
    if expected_identity is not None and identity != expected_identity:
        raise RuntimeError("Linked folder root identity changed")

    found: dict[str, dict] = {}
    mount_points = _mount_points()
    root_identities = frozenset({identity}) if identity[1] not in (None, 0) else frozenset()
    pending = [(root, frozenset({_path_key(root)}), root_identities, 0)]
    while pending:
        directory, ancestor_paths, ancestor_identities, depth = pending.pop()
        with os.scandir(directory) as entries:
            for entry in entries:
                full = entry.path
                if entry.is_symlink():
                    continue
                if entry.is_dir(follow_symlinks = False):
                    resolved = os.path.realpath(full)
                    if (
                        not _is_within(root, resolved)
                        or contains_sensitive_path_component(os.path.relpath(resolved, root))
                        or is_denied_system_path(resolved)

View on GitHub (pinned to 203007d190)

Solutions

  1. Restore the original directory (same inode) or remove and re-register the linked folder so the stored identity matches the new one.
  2. For mounts with unstable device ids, prefer a stable local path or a filesystem with persistent ids.
  3. Surface this error to the user as 'folder changed, please re-link' rather than retrying.
Defensive patterns

Strategy: validation

Validate before calling

import os, stat

def root_identity_matches(path: str, expected: tuple[int, int]) -> bool:
    st = os.stat(path)
    return (st.st_dev, st.st_ino) == expected

Try / catch

try:
    run_sync_job(folder_id)
except RuntimeError as e:
    if "root identity changed" in str(e):
        mark_folder_needs_relink(folder_id)  # do NOT blind-retry
    else:
        raise

Prevention

When it happens

Trigger: A sync job calling _scan(root, expected_identity) after the folder was deleted and recreated (new inode), moved across filesystems, or the storage was remounted with a different device id (some network/overlay filesystems change st_dev per mount).

Common situations: Backup/restore tools that recreate directories; Docker overlay mounts with unstable device ids; NAS remounts; user 'refreshing' a folder by delete+recreate.

Related errors


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