{"record":{"id":"581bea1938307d6e","repo":"jamiepine/voicebox","slug":"failed-to-delete-model-cache-directory-str-e","errorCode":null,"errorMessage":"Failed to delete model cache directory: {str(e)}","messagePattern":"Failed to delete model cache directory: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"backend/routes/models.py","lineNumber":471,"sourceCode":"    config = get_model_config(model_name)\n    if not config:\n        raise HTTPException(status_code=400, detail=f\"Unknown model: {model_name}\")\n\n    hf_repo_id = config.hf_repo_id\n\n    try:\n        unload_model_by_config(config)\n\n        cache_dir = hf_constants.HF_HUB_CACHE\n        repo_cache_dir = Path(cache_dir) / (\"models--\" + hf_repo_id.replace(\"/\", \"--\"))\n\n        if not repo_cache_dir.exists():\n            raise HTTPException(status_code=404, detail=f\"Model {model_name} not found in cache\")\n\n        try:\n            shutil.rmtree(repo_cache_dir)\n        except OSError as e:\n            raise HTTPException(status_code=500, detail=f\"Failed to delete model cache directory: {str(e)}\")\n\n        return {\"message\": f\"Model {model_name} deleted successfully\"}\n\n    except HTTPException:\n        raise\n    except Exception as e:\n        raise HTTPException(status_code=500, detail=f\"Failed to delete model: {str(e)}\")\n","sourceCodeStart":453,"sourceCodeEnd":479,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/routes/models.py#L453-L479","documentation":"500 from DELETE /models/{model_name} when shutil.rmtree(repo_cache_dir) raises OSError. The route catches OSError specifically and forwards detail=f'Failed to delete model cache directory: {str(e)}'. Common OSError causes: permission denied, read-only mount, a file in the tree being open/locked by another process (model still memory-mapped by a running backend), or a path component vanishing mid-delete.","triggerScenarios":"Deleting a model whose weights are still memory-mapped by a loaded backend (the route does call unload_model_by_config first, but a concurrent generate can re-map files); deleting from a read-only or permission-restricted volume; antivirus/OS locking blob files on Windows; NFS/SMB stale file handles.","commonSituations":"Windows file locking when the model is still loaded; Linux process holding the safetensors mmap; destination on a FAT/exFAT external drive with permission quirks; user account lacks delete permission on the cache; concurrent /generate re-loads the model during delete.","solutions":["Ensure the model is fully unloaded and no generate/speak/transcribe call is in flight — the route attempts unload but a concurrent load can race it.","Run the backend with an account that has delete permission on HF_HUB_CACHE; on external drives check mount options (read-write, proper uid).","On Windows, close any process that may have the files open (including file explorers previewing blobs) and retry.","If the directory is partially deleted, manually rm -rf the remnants and re-download."],"exampleFix":"# before — delete while model still loaded / mapped\ncurl -X DELETE http://localhost:8000/models/qwen3-1.7b  # OSError: text file busy\n# after — unload, wait, then delete\ncurl -X POST http://localhost:8000/models/qwen3-1.7b/unload\n# wait for any in-flight /generate to finish, then:\ncurl -X DELETE http://localhost:8000/models/qwen3-1.7b","handlingStrategy":"try-catch","validationCode":"async function safeDelete(name: string) {\n  // ensure the model is unloaded and no generation is in flight first\n  await fetch(`/models/${encodeURIComponent(name)}/unload`, {method:'POST'}).catch(()=>{});\n  await waitForNoInflight(name);\n  return await fetch(`/models/${encodeURIComponent(name)}`, {method:'DELETE'});\n}","typeGuard":null,"tryCatchPattern":"try {\n  await fetch(`/models/${name}`, {method:'DELETE'});\n} catch (e) {\n  const d = e.response?.detail ?? '';\n  if (e.response?.status === 500 && /Failed to delete model cache directory/.test(d)) {\n    if (/busy|locked|permission|read-only/i.test(d)) {\n      // close holders / fix perms, then retry once\n      await new Promise(r => setTimeout(r, 1000));\n      await fetch(`/models/${name}`, {method:'DELETE'});\n    } else throw e;\n  } else throw e;\n}","preventionTips":["Always unload the model and wait for in-flight generation to finish before deleting.","Run the backend with delete permission on the HF cache; on Windows close processes holding the files.","Avoid deleting from read-only or quirky filesystems (FAT/exFAT, locked network mounts)."],"tags":["models","delete","filesystem","permissions","http-500","oserror","concurrency"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}