{"record":{"id":"6ab288a27c1bde41","repo":"unslothai/unsloth","slug":"gguf-variant-deletion-requires-an-export-directory","errorCode":null,"errorMessage":"GGUF variant deletion requires an export directory","messagePattern":"GGUF variant deletion requires an export directory","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"studio/backend/routes/models.py","lineNumber":3118,"sourceCode":"                for lid in getattr(backend, \"loading_repo_ids\", tuple)()\n            ):\n                raise HTTPException(\n                    status_code = 409,\n                    detail = \"Cannot delete a model while it is loading\",\n                )\n        except HTTPException:\n            raise\n        except Exception as e:\n            logger.warning(\"Could not check the %s model before delete: %s\", label, e)\n            raise HTTPException(\n                status_code = 503,\n                detail = \"Could not verify model load status before deleting\",\n            ) from e\n\n    try:\n        if export_type == \"gguf\" and gguf_variant:\n            if not target_path.is_dir():\n                raise HTTPException(\n                    status_code = 400,\n                    detail = \"GGUF variant deletion requires an export directory\",\n                )\n            deleted_count, deleted_bytes = _delete_gguf_variant_files(\n                target_path,\n                gguf_variant,\n            )\n            if deleted_count == 0:\n                raise HTTPException(\n                    status_code = 404,\n                    detail = f\"Variant {gguf_variant} not found on disk\",\n                )\n            try:\n                if not any(target_path.iterdir()):\n                    target_path.rmdir()\n                    _prune_empty_parents(target_path, allowed_root)\n            except OSError:\n                pass","sourceCodeStart":3100,"sourceCodeEnd":3136,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/routes/models.py#L3100-L3136","documentation":"400 raised on the GGUF per-variant delete branch (models.py:3118): export_type=\"gguf\" and gguf_variant are set, all load guards passed, but target_path is not a directory. _delete_gguf_variant_files rglobs an export directory for the variant's files, so it requires the export dir (e.g. exports/my-model-gguf), not a single .gguf file path — note that earlier, when the path ends in .gguf, the code moved the target to raw_path.parent for the containment checks, but this branch validates the directory explicitly.","triggerScenarios":"Passing the .gguf file itself as model_path ({'model_path': '.../model-Q4_K_M.gguf', 'export_type': 'gguf', 'gguf_variant': 'Q4_K_M'}) when path handling did not rewrite it (e.g. different suffix casing or a non-.gguf filename); passing a model path that is a checkpoint directory from source=\"training\" while still setting export_type=\"gguf\"; the export dir having been replaced by a file/symlink after the UI scan.","commonSituations":"Confusing 'delete this one .gguf file' semantics with the endpoint's 'delete one quant variant from an export directory' semantics; reusing a training-source path with GGUF parameters; stale UI rows after manual filesystem edits.","solutions":["Pass the export directory as model_path: exports/my-model-gguf (not the individual .gguf file).","Drop export_type/gguf_variant entirely if you intend a plain directory or single-file delete.","Verify with Path(model_path).is_dir() before sending when export_type is gguf.","If a single GGUF file really must go and it is not a managed export dir, delete export_type from the payload so the non-variant branch handles it."],"exampleFix":"# before\nrequests.delete('/delete-finetuned', json={'model_path': '/exports/m/model-Q4_K_M.gguf', 'source': 'exported', 'export_type': 'gguf', 'gguf_variant': 'Q4_K_M'})\n# after\nrequests.delete('/delete-finetuned', json={'model_path': '/exports/m', 'source': 'exported', 'export_type': 'gguf', 'gguf_variant': 'Q4_K_M'})","handlingStrategy":"validation","validationCode":"if payload.get('export_type') == 'gguf' and payload.get('gguf_variant'):\n    p = Path(payload['model_path']).expanduser()\n    if p.suffix.lower() == '.gguf':\n        payload = {**payload, 'model_path': str(p.parent)}  # send the export dir\n    if not Path(payload['model_path']).is_dir():\n        raise ValueError('GGUF variant deletion needs the export directory, not a file')","typeGuard":"def is_gguf_variant_delete_ready(payload: dict) -> bool:\n    if payload.get('export_type') != 'gguf' or not payload.get('gguf_variant'): return True  # not this branch\n    p = Path(payload['model_path']).expanduser()\n    return p.is_dir()","tryCatchPattern":null,"preventionTips":["For per-variant GGUF deletes, always send the export directory as model_path.","Validate is_dir() client-side when export_type is gguf and a variant is supplied.","Use the path exactly as the export scan advertises it."],"tags":["fastapi","unsloth","gguf","validation","filesystem","model-deletion"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}