{"record":{"id":"34de20404cb2e780","repo":"jamiepine/voicebox","slug":"cuda-backend-download-already-in-progress","errorCode":null,"errorMessage":"CUDA backend download already in progress","messagePattern":"CUDA backend download already in progress","errorType":"http","errorClass":"HTTPException","httpStatus":409,"severity":"warning","filePath":"backend/routes/cuda.py","lineNumber":39,"sourceCode":"    return cuda.get_cuda_status()\n\n\n@router.post(\"/backend/download-cuda\")\nasync def download_cuda_backend():\n    \"\"\"Download the CUDA backend binary.\"\"\"\n    from ..services import cuda\n\n    unsupported_reason = cuda.get_cuda_download_unsupported_reason()\n    if unsupported_reason:\n        raise HTTPException(status_code=409, detail=unsupported_reason)\n\n    if cuda.get_cuda_binary_path() is not None:\n        raise HTTPException(status_code=409, detail=\"CUDA backend already downloaded\")\n\n    progress_manager = get_progress_manager()\n    existing = progress_manager.get_progress(cuda.PROGRESS_KEY)\n    if existing and existing.get(\"status\") == \"downloading\":\n        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(","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/routes/cuda.py#L21-L57","documentation":"HTTP 409 raised by POST /backend/download-cuda when the progress manager reports an existing entry for the 'cuda-backend' key with status 'downloading'. This guards against kicking off a second concurrent download while one is already in flight. The check is advisory (a TOCTOU window exists that the service-level _download_lock also covers), so the response means: a download is already running, subscribe to progress instead.","triggerScenarios":"Calling POST /backend/download-cuda while a prior download (manual or auto-update) is still running.","commonSituations":"User double-clicks the download button; the startup auto-update is mid-download when the user also clicks download; a retry fired before the first request finished.","solutions":["Subscribe to GET /backend/cuda-progress (SSE) to observe the in-flight download instead of re-POSTing.","Disable the download button while status.downloading is true.","Debounce/dedupe the download action in the client."],"exampleFix":"// before\nsetInterval(() => fetch('/backend/download-cuda', { method: 'POST' }), 5000);\n// after\nconst status = await fetch('/backend/cuda').then(r => r.json());\nif (status.downloading) { /* attach to SSE progress */ return; }\nawait fetch('/backend/download-cuda', { method: 'POST' });","handlingStrategy":"validation","validationCode":"const status = await fetch('/backend/cuda').then(r => r.json());\nif (status.downloading) throw new Error('download already running');","typeGuard":"function isCudaDownloading(s): s is { downloading: true } { return s?.downloading === true; }","tryCatchPattern":"const r = await fetch('/backend/download-cuda', { method:'POST' });\nif (r.status === 409 && (await r.json()).detail.includes('in progress')) { /* subscribe to SSE progress */ }","preventionTips":["Disable the download button while status.downloading is true.","Dedupe/debounce the download action.","Subscribe to /backend/cuda-progress instead of re-POSTing."],"tags":["cuda","http","concurrency","conflict","fastapi"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}