{"record":{"id":"874007592675d001","repo":"unslothai/unsloth","slug":"model-path-is-required","errorCode":null,"errorMessage":"model_path is required","messagePattern":"model_path is required","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"studio/backend/routes/models.py","lineNumber":2906,"sourceCode":"    model_path: str = Body(...),\n    source: str = Body(...),\n    export_type: Optional[str] = Body(None),\n    gguf_variant: Optional[str] = Body(None),\n    current_subject: str = Depends(get_current_subject),\n):\n    \"\"\"Delete an Unsloth-trained or exported model from disk.\n\n    Only paths under Unsloth's outputs/exports roots are accepted.\n    Exported GGUF entries can delete one quant variant at a time.\n    \"\"\"\n    if source not in {\"training\", \"exported\"}:\n        raise HTTPException(\n            status_code = 400,\n            detail = \"Only trained or exported Unsloth models can be deleted\",\n        )\n\n    if not model_path or not model_path.strip():\n        raise HTTPException(status_code = 400, detail = \"model_path is required\")\n\n    if export_type == \"gguf\" and not gguf_variant:\n        raise HTTPException(\n            status_code = 400,\n            detail = \"gguf_variant is required when export_type is 'gguf'\",\n        )\n\n    raw_path = Path(model_path).expanduser()\n    if source == \"training\":\n        target_path = raw_path\n        allowed_root = outputs_root()\n    else:\n        allowed_root = exports_root()\n        target_path = (\n            raw_path.parent\n            if export_type == \"gguf\" and raw_path.suffix.lower() == \".gguf\"\n            else raw_path\n        )","sourceCodeStart":2888,"sourceCodeEnd":2924,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/routes/models.py#L2888-L2924","documentation":"Raised by the DELETE /delete-finetuned endpoint in Unsloth Studio (delete_finetuned_model, studio/backend/routes/models.py:2887) when the request body omits model_path or sends only whitespace. model_path is a required Body(...) parameter of type str, so FastAPI would 422 on a missing key; this 400 fires when the key is present but empty or blank. It is a pure client-input validation error, nothing has been touched on disk.","triggerScenarios":"Calling DELETE /delete-finetuned with {\"source\": \"training\", \"model_path\": \"\"} or {\"model_path\": \"   \"} or a model_path of null coerced to empty string; also sending the path under a wrong body key (e.g. \"path\") after disabling FastAPI's strict validation, or a UI row that failed to populate its path field.","commonSituations":"Frontend sends the delete request before the selected row's model_path is bound; the model was recorded in state with an empty path because a scan result was pruned; scripted cleanup clients built from a template that renamed the field.","solutions":["Send a non-blank model_path in the JSON body, e.g. {\"model_path\": \"/unsloth-outputs/my-run/checkpoint-100\", \"source\": \"training\"}.","Confirm the field name is exactly model_path (not path or model_dir) and the Content-Type is application/json on the DELETE request.","If driving from the UI, re-scan the models list so rows carry a real path, and re-select the row before clicking delete.","Trim the value client-side and skip the call entirely when it is empty."],"exampleFix":"// before\nawait fetch('/delete-finetuned', {method: 'DELETE', body: JSON.stringify({source: 'training', model_path: selectedRow?.path ?? ''})});\n// after\nif (!selectedRow?.path?.trim()) throw new Error('No model selected');\nawait fetch('/delete-finetuned', {method: 'DELETE', body: JSON.stringify({source: 'training', model_path: selectedRow.path})});","handlingStrategy":"validation","validationCode":"function buildDeletePayload(row) {\n  const model_path = (row?.model_path ?? '').trim();\n  if (!model_path) throw new Error('model_path is required: select a model row first');\n  return { model_path, source: row.source };\n}","typeGuard":"function isDeletableRow(row) {\n  return Boolean(row && typeof row.model_path === 'string' && row.model_path.trim() && ['training', 'exported'].includes(row.source));\n}","tryCatchPattern":"try { await api.delete('/delete-finetuned', { data: payload }); } catch (e) { if (e.status === 400 && e.detail === 'model_path is required') { /* reselect row, do not retry blindly */ } else throw e; }","preventionTips":["Bind the delete button's disabled state to a non-empty trimmed model_path.","Send exactly the path returned by the models scan API, never a user-typed string.","Keep one canonical name for the field (model_path) across UI, scripts, and API wrappers."],"tags":["fastapi","unsloth","validation","request-body","model-deletion"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}