{"record":{"id":"57cdd8d38718a73b","repo":"jamiepine/voicebox","slug":"cannot-delete-cuda-backend-while-it-is-active-swi","errorCode":null,"errorMessage":"Cannot delete CUDA backend while it is active. Switch to CPU first.","messagePattern":"Cannot delete CUDA backend while it is active\\. Switch to CPU first\\.","errorType":"http","errorClass":"HTTPException","httpStatus":409,"severity":"warning","filePath":"backend/routes/cuda.py","lineNumber":57,"sourceCode":"        raise HTTPException(status_code=409, detail=\"CUDA backend download already in progress\")\n\n    async def _download():\n        try:\n            await cuda.download_cuda_binary()\n        except Exception as e:\n            logger.error(\"CUDA download failed: %s\", e)\n\n    create_background_task(_download())\n    return {\"message\": \"CUDA backend download started\", \"progress_key\": \"cuda-backend\"}\n\n\n@router.delete(\"/backend/cuda\")\nasync def delete_cuda_backend():\n    \"\"\"Delete the downloaded CUDA backend binary.\"\"\"\n    from ..services import cuda\n\n    if cuda.is_cuda_active():\n        raise HTTPException(\n            status_code=409,\n            detail=\"Cannot delete CUDA backend while it is active. Switch to CPU first.\",\n        )\n\n    deleted = await cuda.delete_cuda_binary()\n    if not deleted:\n        raise HTTPException(status_code=404, detail=\"No CUDA backend found to delete\")\n\n    return {\"message\": \"CUDA backend deleted\"}\n\n\n@router.get(\"/backend/cuda-progress\")\nasync def get_cuda_download_progress():\n    \"\"\"Get CUDA backend download progress via Server-Sent Events.\"\"\"\n    progress_manager = get_progress_manager()\n\n    async def event_generator():\n        async for event in progress_manager.subscribe(\"cuda-backend\"):","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/routes/cuda.py#L39-L75","documentation":"HTTP 409 raised by DELETE /backend/cuda when cuda.is_cuda_active() returns true, i.e. the current process is itself the CUDA binary (env var VOICEBOX_BACKEND_VARIANT == 'cuda'). Deleting the onedir bundle out from under a running CUDA process would corrupt the active backend, so the endpoint refuses and instructs you to switch to CPU first.","triggerScenarios":"Calling DELETE /backend/cuda while the server is running as the CUDA variant (the process was launched from the downloaded CUDA executable).","commonSituations":"Trying to free disk or force an update without first restarting on CPU; an update script running inside the CUDA backend process.","solutions":["Restart the server on the CPU backend (unset/switch VOICEBOX_BACKEND_VARIANT), then issue DELETE /backend/cuda.","For upgrades, prefer letting startup auto-update handle it rather than manual delete while active.","Check GET /backend/cuda status.active before offering the delete action."],"exampleFix":"// before\nawait fetch('/backend/cuda', { method: 'DELETE' });\n// after\nconst status = await fetch('/backend/cuda').then(r => r.json());\nif (status.active) { alert('Switch to the CPU backend before deleting CUDA'); return; }\nawait fetch('/backend/cuda', { method: 'DELETE' });","handlingStrategy":"validation","validationCode":"const status = await fetch('/backend/cuda').then(r => r.json());\nif (status.active) throw new Error('switch to CPU backend before deleting');","typeGuard":"function isCudaActive(s): s is { active: true } { return s?.active === true; }","tryCatchPattern":"const r = await fetch('/backend/cuda', { method:'DELETE' });\nif (r.status === 409) { const { detail } = await r.json(); /* instructs CPU switch */ }","preventionTips":["Restart on the CPU backend before deleting CUDA.","Hide delete while status.active is true.","Prefer auto-update over manual delete-while-running."],"tags":["cuda","http","conflict","state","fastapi"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}