{"record":{"id":"ddbc7345eaa9a714","repo":"unslothai/unsloth","slug":"rejection-message","errorCode":null,"errorMessage":"{rejection_message}","messagePattern":"\\{rejection_message\\}","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"studio/backend/routes/models.py","lineNumber":1218,"sourceCode":"    \"\"\"List all registered custom model scan folders.\"\"\"\n    from storage.studio_db import list_scan_folders\n    return {\"folders\": list_scan_folders()}\n\n\n@router.post(\"/scan-folders\", response_model = ScanFolderInfo, status_code = 201)\nasync def add_scan_folder_endpoint(\n    body: AddScanFolderRequest, current_subject: str = Depends(get_current_subject)\n):\n    \"\"\"Register a new directory to scan for local models.\"\"\"\n    from storage.studio_db import add_scan_folder_with_status\n\n    try:\n        folder, inserted = await asyncio.to_thread(add_scan_folder_with_status, body.path)\n    except ValueError as e:\n        logger.warning(\"Scan folder rejected: %s (path=%s)\", e, body.path)\n        # Forward the curated, path-free validation message.\n        rejection_message = str(e)\n        raise HTTPException(status_code = 400, detail = rejection_message)\n    logger.info(\"Scan folder added: %s\", folder.get(\"path\"))\n    if inserted:\n        from core.inference.local_model_resolver import invalidate_index, warm_index_soon\n        await asyncio.to_thread(invalidate_index)\n        warm_index_soon()\n    return folder\n\n\n@router.delete(\"/scan-folders/{folder_id}\")\nasync def remove_scan_folder_endpoint(\n    folder_id: int, current_subject: str = Depends(get_current_subject)\n):\n    \"\"\"Remove a registered custom scan folder.\"\"\"\n    from storage.studio_db import remove_scan_folder\n\n    removed = await asyncio.to_thread(remove_scan_folder, folder_id)\n    if removed:\n        logger.info(\"Scan folder removed: id=%s\", folder_id)","sourceCodeStart":1200,"sourceCodeEnd":1236,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/routes/models.py#L1200-L1236","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Read the 400 detail — it is the validator's curated reason and names the exact problem without the path.","Confirm the path exists and is a directory on the backend host (not the browser machine): os.path.isdir from the server's perspective.","Use the absolute, expanded form of the path (no ~, no trailing oddities).","If registering a sensitive/system location, choose a normal data directory instead."],"exampleFix":"# before\nclient.post('/api/models/scan-folders', json={'path': '~/models'})  # may 400\n\n# after\nimport os\nclient.post('/api/models/scan-folders', json={'path': os.path.expanduser('~/models')})","handlingStrategy":"validation","validationCode":"import os\n\npath = os.path.realpath(os.path.abspath(os.path.expanduser(raw_path)))\nif not os.path.isdir(path):\n    raise ValueError(f'not a readable directory: {raw_path}')\nclient.post('/api/models/scan-folders', json={'path': path})","typeGuard":null,"tryCatchPattern":"try:\n    folder = client.post('/api/models/scan-folders', json={'path': p}).json()\nexcept HTTPError as e:\n    if e.response.status_code == 400:\n        show_user(e.response.json()['detail'])  # curated, path-free reason\n    else:\n        raise","preventionTips":["Validate the path exists on the backend host before submitting.","Send absolute expanded paths.","Show the 400 detail verbatim to the user — it is curated to be user-facing."],"tags":["models","scan-folders","validation","api"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}