unslothai/unsloth · error · HTTPException
Permission denied reading {current.name}
Error message
Permission denied reading {current.name} What it means
Raised as a 403 by _match_browse_child when the backend, while walking from an allowlist root toward the requested browse path, hits a PermissionError calling iterdir() on an intermediate directory. It means the backend process lacks read permission on that directory, so it cannot confirm the next path component exists.
Source
Thrown at studio/backend/routes/models.py:1638
if rel_text == ".." or rel_text.startswith(f"..{os.sep}"):
return None
parts = [part for part in rel_text.split(os.sep) if part not in ("", ".")]
altsep = os.altsep
for part in parts:
if part == ".." or os.sep in part or (altsep and altsep in part):
return None
return parts
def _match_browse_child(current: Path, name: str) -> Optional[Path]:
"""Return the immediate child named ``name`` under ``current``."""
try:
for child in current.iterdir():
if child.name == name:
return child
except PermissionError:
raise HTTPException(
status_code = 403,
detail = f"Permission denied reading {current.name}",
) from None
except OSError as exc:
logger.warning("browse-folders: could not read %s: %s", current, exc, exc_info = True)
raise HTTPException(
status_code = 500,
detail = f"Could not read {os.path.basename(str(current))}",
) from exc
return None
def _resolve_browse_target(path: Optional[str], allowed_roots: list[Path]) -> Path:
"""Resolve a requested browse path by walking from trusted allowlist roots."""
from storage.studio_db import (
contains_sensitive_path_component,
is_denied_system_path,
)View on GitHub (pinned to 203007d190)
Solutions
- Grant the backend process read+execute permission on the blocking directory: chmod a+rx /restricted/dir or adjust the ACL.
- Run the backend as a user that can traverse the path, or move the models under a directory the backend can read.
- Register the deepest readable directory as a scan folder via POST /api/models/scan-folders so the walk starts there and skips unreadable parents.
Example fix
# before: walk must pass through /srv/private (mode 700, other user) GET /api/models/browse-folders?path=/srv/private/models # 403 # after (backend user can traverse): # chmod o+rx /srv/private GET /api/models/browse-folders?path=/srv/private/models
Defensive patterns
Strategy: validation
Validate before calling
import os
def readable(d: str) -> bool:
try:
os.listdir(d); return True
except PermissionError:
return False
path = '/srv/private/models'
if not all(readable(p) for p in iter_parents(path)):
raise PermissionError('backend cannot traverse path — fix perms or register a nearer scan folder') Try / catch
try:
entries = browse(path)
except HTTPError as e:
if e.response.status_code == 403 and 'Permission denied' in e.response.json()['detail']:
show_user('Grant the backend read access to the parent directory')
else: raise Prevention
- Keep model trees owned by (or readable by) the backend user.
- Register the deepest readable directory as a scan folder so walks skip restricted parents.
- Avoid nesting model data under directories with 700 perms belonging to other users.
When it happens
Trigger: GET browse-folders with a path whose route from an allowed root passes through a directory owned by another user or with mode 700; running the studio backend as a user without read access; Windows ACLs denying list permission on a parent folder.
Common situations: Models stored under a sibling user's home or a restricted mount; containerized backend running as a different uid than the directory owner; macOS/Linux permission tightening on parent dirs.
Related errors
- Could not read {os.path.basename(str(current))}
- Permission denied reading {os.path.basename(str(target))}
- Path does not exist: {os.path.basename(requested_path)}
- Invalid path
- Could not read {os.path.basename(str(target))}
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/2724e1526c474a3d.
Report an issue: GitHub.