unslothai/unsloth · error · HTTPException
Directory not allowed
Error message
Directory not allowed
What it means
Raised as a 403 by the local-models listing endpoint when the requested models_dir, after expanduser + realpath, is neither equal to nor a subdirectory of any allowed root (home directory, studio root, outputs root, registered scan folders). The check uses resolved paths on both sides, so symlink tricks do not bypass it; the trusted allowlist root is used for the scan, never the user-supplied path.
Source
Thrown at studio/backend/routes/models.py:1172
if _safe_is_dir(legacy_hf):
allowed_roots.append(legacy_hf)
if _safe_is_dir(hf_default):
allowed_roots.append(hf_default)
try:
from utils.paths import studio_root, outputs_root
allowed_roots.extend([studio_root(), outputs_root()])
except Exception:
pass
requested = os.path.realpath(os.path.expanduser(models_dir))
models_root = None
for root in allowed_roots:
root_str = os.path.realpath(str(root))
if requested == root_str or requested.startswith(root_str + os.sep):
models_root = root # trusted root, not the user-supplied path
break
if models_root is None:
raise HTTPException(
status_code = 403,
detail = "Directory not allowed",
)
try:
models = await _shared_compat_local_inventory_scan(models_root, sources)
# Tag each model with its task so the Images picker can filter to diffusion.
models = [m.model_copy(update = {"task": _local_model_task(m)}) for m in models]
return LocalModelListResponse(
models_dir = str(models_root),
hf_cache_dir = str(hf_cache_dir),
lmstudio_dirs = [str(d) for d in lm_dirs],
models = models,
)
except Exception as e:
raise log_and_http_error(
e,View on GitHub (pinned to 203007d190)
Solutions
- Register the directory first via POST /api/models/scan-folders with the absolute path, then retry the models listing.
- Point models_dir at a directory under your home folder, the studio root, or the outputs root.
- Verify the realpath: the comparison uses os.path.realpath on both sides, so confirm the resolved location (e.g. readlink -f /your/path) is actually inside an allowed root.
- Check for symlinks in the path whose targets escape the allowlist and register the real target instead.
Example fix
# before
resp = client.get('/api/models/local', params={'models_dir': '/mnt/bigdrive/models'}) # 403
# after
client.post('/api/models/scan-folders', json={'path': '/mnt/bigdrive/models'})
resp = client.get('/api/models/local', params={'models_dir': '/mnt/bigdrive/models'}) Defensive patterns
Strategy: validation
Validate before calling
import os
def allowed(models_dir: str, scan_folders: list[str]) -> bool:
req = os.path.realpath(os.path.expanduser(models_dir))
roots = [os.path.realpath(os.path.expanduser('~'))] + [os.path.realpath(f) for f in scan_folders]
return any(req == r or req.startswith(r + os.sep) for r in roots) Try / catch
try:
resp = client.get('/api/models/local', params={'models_dir': d})
except HTTPError as e:
if e.response.status_code == 403:
register_scan_folder(d); resp = client.get('/api/models/local', params={'models_dir': d})
else: raise Prevention
- Always register non-home model directories via POST /api/models/scan-folders before listing.
- Send absolute paths; let the server do ~ expansion consistently.
- Remember the check is on realpath — avoid symlinks that resolve outside allowed roots.
When it happens
Trigger: GET /api/models/local?models_dir=/etc or any path outside the allowlist; a path containing '..' that resolves outside a root; a symlink whose realpath target lies outside all allowed roots; a directory that was never registered via POST /api/models/scan-folders.
Common situations: Front-end remembers a models directory from another machine/user; user pastes /var/data/models or a mount point outside home; models stored on an external drive not registered as a scan folder; case/format differences in the path that make the prefix check fail.
Related errors
- Path is not in the browseable allowlist. Register it via POS
- local cache path must not contain '..' segments
- Credential or configuration directories are not browseable.
- System directories are not browseable.
- Model path is outside Unsloth storage
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/d2ecf44fa8bf1490.
Report an issue: GitHub.