unslothai/unsloth · error · HTTPException
Only trained or exported Unsloth models can be deleted
Error message
Only trained or exported Unsloth models can be deleted
What it means
Raised as a 400 by the Unsloth model-delete endpoint when the request body's source field is not one of {'training', 'exported'}. The endpoint deliberately deletes only artifacts Unsloth itself created under its outputs/exports roots, so any other source value is refused before path validation begins.
Source
Thrown at studio/backend/routes/models.py:2900
deleted_count += 1
return deleted_count, deleted_bytes
@router.delete("/delete-finetuned")
async def delete_finetuned_model(
model_path: str = Body(...),
source: str = Body(...),
export_type: Optional[str] = Body(None),
gguf_variant: Optional[str] = Body(None),
current_subject: str = Depends(get_current_subject),
):
"""Delete an Unsloth-trained or exported model from disk.
Only paths under Unsloth's outputs/exports roots are accepted.
Exported GGUF entries can delete one quant variant at a time.
"""
if source not in {"training", "exported"}:
raise HTTPException(
status_code = 400,
detail = "Only trained or exported Unsloth models can be deleted",
)
if not model_path or not model_path.strip():
raise HTTPException(status_code = 400, detail = "model_path is required")
if export_type == "gguf" and not gguf_variant:
raise HTTPException(
status_code = 400,
detail = "gguf_variant is required when export_type is 'gguf'",
)
raw_path = Path(model_path).expanduser()
if source == "training":
target_path = raw_path
allowed_root = outputs_root()
else:View on GitHub (pinned to 203007d190)
Solutions
- Send source='training' for Unsloth-trained checkpoints or source='exported' for exported artifacts (including per-variant GGUF deletes with export_type='gguf' and gguf_variant).
- Do not use this endpoint for Hub-downloaded or arbitrary local models — remove those with your own file tooling.
- Check casing: the comparison is exact against the lowercase literals.
Example fix
# before
client.post('/api/models/unsloth/delete', json={'model_path': p, 'source': 'Exported'}) # 400
# after
client.post('/api/models/unsloth/delete', json={'model_path': p, 'source': 'exported', 'export_type': 'gguf', 'gguf_variant': 'Q4_K_M'}) Defensive patterns
Strategy: type-guard
Validate before calling
const DELETABLE_SOURCES = ['training', 'exported'] as const;
if (!DELETABLE_SOURCES.includes(entry.source)) {
throw new Error(`cannot delete ${entry.source} models here — only Unsloth 'training'/'exported' artifacts`);
} Type guard
type UnslothSource = 'training' | 'exported';
function isDeletableSource(s: unknown): s is UnslothSource {
return s === 'training' || s === 'exported';
} Try / catch
try {
await deleteModel({model_path, source});
} catch (e) {
if (e.status === 400 && /Only trained or exported/.test(e.detail)) {
disableDeleteButton(); // entry is not an Unsloth artifact
} else throw e;
} Prevention
- Only show the delete action for entries whose source is exactly 'training' or 'exported' (lowercase).
- Do not reuse this endpoint for Hub or arbitrary local models — manage those separately.
- For GGUF exports always send export_type='gguf' plus gguf_variant.
When it happens
Trigger: POST to the delete endpoint with body {"model_path": ..., "source": "hub"} (or 'custom', 'local', missing) — anything outside the two allowed literals.
Common situations: Front-end delete button wired to a generic model list that includes Hub/local entries; API consumers assuming the endpoint is a general model deleter; typos/casing ('Exported' vs 'exported').
Related errors
- {rejection_message}
- Invalid model snapshot repository ID.
- Unsupported diffusion memory_mode '{value}'. Use one of: {va
- Unsupported text_encoder_quant '{value}'. Use one of: {', '.
- Unsupported diffusion speed_mode '{value}'. Use one of: {',
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/380fd568b292474d.
Report an issue: GitHub.