unslothai/unsloth · error · HTTPException
Permission denied reading {os.path.basename(str(target))}
Error message
Permission denied reading {os.path.basename(str(target))} What it means
Raised as a 403 by the browse-folders listing handler when target.iterdir() throws PermissionError on the final, already-validated target directory (unlike errors 1104/1105, which occur during the walk to the target). The backend can see the directory exists and is allowlisted, but the process lacks read permission to enumerate its entries.
Source
Thrown at studio/backend/routes/models.py:1815
try:
target = _resolve_browse_target(path, allowed_roots)
except HTTPException:
requested_path = _normalize_browse_request_path(path)
if path is not None and path.strip():
logger.warning(
"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
breakView on GitHub (pinned to 203007d190)
Solutions
- Grant read+execute on the directory to the backend user: chmod a+rx /path/to/dir or set the ACL accordingly.
- chown the directory to the user running the studio backend.
- If intentional restriction, browse a different, readable directory.
Example fix
# backend runs as 'beagle', dir owned by root mode 700 # before GET /api/models/browse-folders?path=/data/models # 403 # after (root shell) # chown -R beagle:beagle /data/models GET /api/models/browse-folders?path=/data/models
Defensive patterns
Strategy: validation
Validate before calling
import os
def listable(d: str) -> bool:
try:
it = os.scandir(d); next(iter(it), None); return True
except PermissionError:
return False
except OSError:
return False
if not listable(target_dir):
raise PermissionError(f'backend lacks read access to {target_dir}') Try / catch
try:
entries = browse(dir)
except HTTPError as e:
if e.response.status_code == 403 and 'Permission denied' in e.response.json()['detail']:
show_user('The backend process cannot read this folder — fix ownership/mode and retry.')
else: raise Prevention
- After creating model directories as root or in containers, chown them to the backend user.
- Use chmod a+rx on directories meant to be browsable.
- Verify with `sudo -u <backend-user> ls <dir>` before wiring the folder into the UI.
When it happens
Trigger: GET browse-folders?path=<allowed-dir> where the directory mode/ACL denies read to the backend user; directory owned by root with 700 while studio runs unprivileged.
Common situations: Models directory created by root or a container and not chowned; chmod 600 applied to a directory by mistake; Windows ACLs from a copied folder denying list access.
Related errors
- Permission denied reading {current.name}
- Could not read {os.path.basename(str(current))}
- Path does not exist: {os.path.basename(requested_path)}
- Path is not readable
- Failed to delete dataset from {len(failures)} cache location
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/7e88d752386ab40c.
Report an issue: GitHub.