unslothai/unsloth · warning · ValueError

Path must be a directory, not a file

Error message

Path must be a directory, not a file

What it means

ValueError raised when the fully normalized path (after realpath resolution of symlinks and . /..) exists but is not a directory — i.e. it is a regular file (or special file). The folder-link feature scans directory trees, so registering a file has no meaningful semantics and is rejected explicitly with a message distinguishing it from 'does not exist'.

Source

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

    expanded = os.path.abspath(os.path.expanduser(path))
    try:
        if stat.S_ISLNK(os.lstat(expanded).st_mode):
            raise ValueError("Symbolic-link folders are not allowed")
    except OSError as exc:
        raise ValueError("Path does not exist") from exc
    normalized = os.path.realpath(expanded)
    uploads_root = os.path.realpath(str(rag_uploads_root()))
    if _paths_overlap(_path_key(normalized), _path_key(uploads_root)):
        raise ValueError("The managed RAG uploads folder cannot be linked")

    from hub.storage.scan_folders import (
        contains_sensitive_path_component,
        is_denied_system_path,
    )
    from utils.paths.external_media import is_local_filesystem_root

    if not os.path.isdir(normalized):
        raise ValueError("Path must be a directory, not a file")
    if not os.access(normalized, os.R_OK | os.X_OK):
        raise ValueError("Path is not readable")
    if is_local_filesystem_root(normalized):
        raise ValueError("The filesystem root cannot be registered")
    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:

View on GitHub (pinned to 203007d190)

Solutions

  1. Pass the containing directory of the file you want indexed — the scanner recurses into it.
  2. Configure the client-side folder picker to directory-selection mode.
  3. If a directory was expected, check whether something replaced it with a file of the same name.

Example fix

# before
validate_folder_path('/home/me/docs/report.pdf')

# after
validate_folder_path('/home/me/docs')  # scanner indexes files inside
Defensive patterns

Strategy: validation

Validate before calling

import os

def folder_is_directory(path: str) -> bool:
    expanded = os.path.realpath(os.path.abspath(os.path.expanduser(path)))
    return os.path.isdir(expanded)

Try / catch

try:
    validate_folder_path(path)
except ValueError as e:
    if "must be a directory" not in str(e):
        raise
    import os
    validate_folder_path(os.path.dirname(os.path.abspath(path)))  # register parent dir

Prevention

When it happens

Trigger: Calling validate_folder_path on a path to a .txt/.pdf file instead of its containing folder; drag-and-drop of a file (not folder) from an OS file picker into the link form; a path whose final component was replaced by a file after earlier validation.

Common situations: Users pasting a document path instead of its directory; file pickers configured without directory-only mode; frontend passing the wrong field (file path instead of folder path).

Related errors


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