unslothai/unsloth · error · RuntimeError
Linked folder is no longer a regular directory
Error message
Linked folder is no longer a regular directory
What it means
RuntimeError raised by _root_identity on a registered folder when lstat succeeds but the path is now a symlink or no longer a directory (e.g. someone replaced the folder with a symlink after registration). This is a TOCTOU defense: identity is keyed on (st_dev, st_ino) of a real directory, and a swapped-in symlink would make the recorded identity point somewhere else, so the sync refuses to proceed. Unlike initial validation (ValueError), this fires on persisted links during sync, indicating the folder changed out from under the registry.
Source
Thrown at studio/backend/core/rag/folder_sync.py:171
try:
if Path(normalized) == Path.home().resolve():
raise ValueError("The entire home folder cannot be registered")
except RuntimeError:
pass
if contains_sensitive_path_component(normalized):
raise ValueError("Credential or configuration directories are not allowed")
if is_denied_system_path(normalized):
raise ValueError("System directories are not allowed")
return normalized
def _root_identity(root: str) -> tuple[int, int]:
try:
root_stat = os.lstat(root)
except OSError as exc:
raise RuntimeError("Linked folder is unavailable") from exc
if stat.S_ISLNK(root_stat.st_mode) or not stat.S_ISDIR(root_stat.st_mode):
raise RuntimeError("Linked folder is no longer a regular directory")
if os.path.normcase(os.path.realpath(root)) != os.path.normcase(root):
raise RuntimeError("Linked folder no longer resolves to its registered path")
return root_stat.st_dev, root_stat.st_ino
def _store_identity(identity: tuple[int, int]) -> tuple[int | str, int | str]:
return tuple(value if value <= _SQLITE_INTEGER_MAX else f"x{value:x}" for value in identity)
def _file_identity(metadata: dict) -> tuple[int | str, int | str]:
return _store_identity((metadata["device"], metadata["inode"]))
def _load_identity(device_id: int | str, file_id: int | str) -> tuple[int, int]:
def load(value: int | str) -> int:
return (
int(value[1:], 16) if isinstance(value, str) and value.startswith("x") else int(value)
)View on GitHub (pinned to 203007d190)
Solutions
- Remove the symlink and restore a real directory at the registered path, then re-sync.
- If the location genuinely moved, unregister the old link and register the new path through validate_folder_path (which accepts the real directory).
- Catch this RuntimeError in sync loops and quarantine the offending link rather than aborting all syncs.
Example fix
# before
rm -rf /data/docs && ln -s /mnt/big/docs /data/docs # sync now fails
# after
rm /data/docs # remove the symlink
unregister_folder('/data/docs') # drop stale record
register_folder('/mnt/big/docs') # register the real directory Defensive patterns
Strategy: try-catch
Validate before calling
import os, stat
def folder_still_real_directory(root: str) -> bool:
try:
st = os.lstat(root)
except OSError:
return False
return not stat.S_ISLNK(st.st_mode) and stat.S_ISDIR(st.st_mode) Try / catch
try:
sync(link)
except RuntimeError as e:
if "no longer a regular directory" not in str(e):
raise
logger.warning("%s replaced with non-directory; re-register the real path", link.path)
unregister(link)
notify_user_reregister(link.path) Prevention
- Re-check lstat identity (dev, ino) before each sync so swapped folders fail fast.
- Forbid scripts/users from replacing registered folders with symlinks; alert on change.
- When relocating data, always unregister the old link and register the new real path via validate_folder_path.
When it happens
Trigger: Deleting the registered folder and recreating the same name as a symlink (or as a file); a mount tool replacing the directory with a symlink to a cache; macOS Finder alias creation over the original folder.
Common situations: Users 'reorganizing' by symlinking the old path to a new location; dev environments where a script recreates directories as links; automation that swaps folder contents via symlink indirection.
Related errors
- Path is not in the browseable allowlist. Register it via POS
- Model path is outside Unsloth storage
- Native path is no longer a regular file.
- GGUF conversion produced a symlink, refusing to relocate it:
- Symbolic-link folders are not allowed
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/42cc52a3eec0f0f5.
Report an issue: GitHub.