unslothai/unsloth · error · HTTPException

Could not read {os.path.basename(str(target))}

Error message

Could not read {os.path.basename(str(target))}

What it means

Raised as a 500 by the browse-folders listing handler when target.iterdir() throws a non-permission OSError on the validated target — the directory enumeration itself failed at the OS level. The detailed exception is logged server-side (with exc_info); the client receives only a basename-scoped 'Could not read' message to avoid leaking paths or raw OS errors.

Source

Thrown at studio/backend/routes/models.py:1821

                "browse-folders: rejected path %r (normalized=%s)",
                path,
                requested_path,
            )
        raise

    entries: list[BrowseEntry] = []
    truncated = False
    visited = 0
    try:
        it = target.iterdir()
    except PermissionError:
        raise HTTPException(
            status_code = 403,
            detail = f"Permission denied reading {os.path.basename(str(target))}",
        )
    except OSError as exc:
        logger.warning("browse-folders: could not read %s: %s", target, exc, exc_info = True)
        raise HTTPException(
            status_code = 500,
            detail = f"Could not read {os.path.basename(str(target))}",
        )

    try:
        for child in it:
            # Bound by *visited*, not *appended*: a cap on len(entries) would never trigger in dirs
            # full of files. Counting visits caps worst-case work at ``_BROWSE_ENTRY_CAP``.
            visited += 1
            if visited > _BROWSE_ENTRY_CAP:
                truncated = True
                break
            try:
                if not child.is_dir():
                    continue
            except OSError:
                continue
            name = child.name

View on GitHub (pinned to 203007d190)

Solutions

  1. Read the backend log line 'browse-folders: could not read' for the underlying errno.
  2. Restore the underlying storage (reconnect drive, remount share, reconnect USB) and retry.
  3. If deleted, refresh the tree from the allowlist root and navigate again.
Defensive patterns

Strategy: fallback

Validate before calling

import os

def readable_now(d: str) -> bool:
    try:
        next(iter(os.scandir(d)), None)
        return True
    except OSError:
        return False

if not readable_now(target_dir):
    entries = load_cached_listing(target_dir)  # degrade gracefully
else:
    entries = browse(target_dir)

Try / catch

try:
    entries = browse(dir)
except HTTPError as e:
    if e.response.status_code == 500 and 'Could not read' in e.response.json()['detail']:
        entries = load_cached_listing(dir)
        warn_user('Folder is temporarily unreadable (storage offline?) — showing cached contents.')
    else: raise

Prevention

When it happens

Trigger: GET browse-folders?path=<dir> when the directory sits on a disconnected mapped drive, stale NFS/SMB mount, failing disk, or was deleted between the resolve step and the iterdir call.

Common situations: Network share dropped while the browse dialog was open; external USB drive unplugged; disk I/O errors; TOCTOU deletion races.

Related errors


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