unslothai/unsloth · error · HTTPException

{rejection_message}

Error message

{rejection_message}

What it means

Raised as a 400 by POST /api/models/scan-folders when add_scan_folder_with_status raises ValueError during registration of a new local-model scan directory. The ValueError message is deliberately curated and path-free (the raw body.path is only logged server-side, never echoed), so the client receives a safe, human-readable rejection reason such as a nonexistent path or a sensitive location.

Source

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

    """List all registered custom model scan folders."""
    from storage.studio_db import list_scan_folders
    return {"folders": list_scan_folders()}


@router.post("/scan-folders", response_model = ScanFolderInfo, status_code = 201)
async def add_scan_folder_endpoint(
    body: AddScanFolderRequest, current_subject: str = Depends(get_current_subject)
):
    """Register a new directory to scan for local models."""
    from storage.studio_db import add_scan_folder_with_status

    try:
        folder, inserted = await asyncio.to_thread(add_scan_folder_with_status, body.path)
    except ValueError as e:
        logger.warning("Scan folder rejected: %s (path=%s)", e, body.path)
        # Forward the curated, path-free validation message.
        rejection_message = str(e)
        raise HTTPException(status_code = 400, detail = rejection_message)
    logger.info("Scan folder added: %s", folder.get("path"))
    if inserted:
        from core.inference.local_model_resolver import invalidate_index, warm_index_soon
        await asyncio.to_thread(invalidate_index)
        warm_index_soon()
    return folder


@router.delete("/scan-folders/{folder_id}")
async def remove_scan_folder_endpoint(
    folder_id: int, current_subject: str = Depends(get_current_subject)
):
    """Remove a registered custom scan folder."""
    from storage.studio_db import remove_scan_folder

    removed = await asyncio.to_thread(remove_scan_folder, folder_id)
    if removed:
        logger.info("Scan folder removed: id=%s", folder_id)

View on GitHub (pinned to 203007d190)

Solutions

  1. Read the 400 detail — it is the validator's curated reason and names the exact problem without the path.
  2. Confirm the path exists and is a directory on the backend host (not the browser machine): os.path.isdir from the server's perspective.
  3. Use the absolute, expanded form of the path (no ~, no trailing oddities).
  4. If registering a sensitive/system location, choose a normal data directory instead.

Example fix

# before
client.post('/api/models/scan-folders', json={'path': '~/models'})  # may 400

# after
import os
client.post('/api/models/scan-folders', json={'path': os.path.expanduser('~/models')})
Defensive patterns

Strategy: validation

Validate before calling

import os

path = os.path.realpath(os.path.abspath(os.path.expanduser(raw_path)))
if not os.path.isdir(path):
    raise ValueError(f'not a readable directory: {raw_path}')
client.post('/api/models/scan-folders', json={'path': path})

Try / catch

try:
    folder = client.post('/api/models/scan-folders', json={'path': p}).json()
except HTTPError as e:
    if e.response.status_code == 400:
        show_user(e.response.json()['detail'])  # curated, path-free reason
    else:
        raise

Prevention

When it happens

Trigger: POST /api/models/scan-folders with body {"path": ...} where the path does not exist, is not absolute, is a file rather than a directory, points at credential/system locations, or duplicates an already-registered folder in a way the validator rejects.

Common situations: Typo in the path; relative path where absolute is required; network/mount path not reachable at registration time; trying to register ~/.ssh or another sensitive directory; front-end sending an empty string.

Related errors


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