{"record":{"id":"28c088e023abed20","repo":"jamiepine/voicebox","slug":"audio-file-not-found-28c088","errorCode":null,"errorMessage":"Audio file not found","messagePattern":"Audio file not found","errorType":"http","errorClass":"HTTPException","httpStatus":404,"severity":"error","filePath":"backend/routes/captures.py","lineNumber":102,"sourceCode":"\n@router.get(\"/captures/{capture_id}\", response_model=models.CaptureResponse)\nasync def get_capture_endpoint(capture_id: str, db: Session = Depends(get_db)):\n    capture = captures_service.get_capture(capture_id, db)\n    if not capture:\n        raise HTTPException(status_code=404, detail=\"Capture not found\")\n    return capture\n\n\n@router.get(\"/captures/{capture_id}/audio\")\nasync def get_capture_audio_endpoint(capture_id: str, db: Session = Depends(get_db)):\n    \"\"\"Stream the original capture audio file.\"\"\"\n    row = db.query(DBCapture).filter(DBCapture.id == capture_id).first()\n    if not row:\n        raise HTTPException(status_code=404, detail=\"Capture not found\")\n\n    audio_path = config.resolve_storage_path(row.audio_path)\n    if audio_path is None or not audio_path.exists():\n        raise HTTPException(status_code=404, detail=\"Audio file not found\")\n\n    return FileResponse(\n        audio_path,\n        media_type=\"audio/wav\",\n        filename=f\"capture_{capture_id}.wav\",\n    )\n\n\n@router.delete(\"/captures/{capture_id}\")\nasync def delete_capture_endpoint(capture_id: str, db: Session = Depends(get_db)):\n    deleted = captures_service.delete_capture(capture_id, db)\n    if not deleted:\n        raise HTTPException(status_code=404, detail=\"Capture not found\")\n    return {\"message\": f\"Capture {capture_id} deleted\"}\n\n\n@router.post(\"/captures/{capture_id}/refine\", response_model=models.CaptureResponse)\nasync def refine_capture_endpoint(","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/routes/captures.py#L84-L120","documentation":"Returned as a 404 from GET /captures/{capture_id}/audio when the DBCapture row exists but the audio file is gone — config.resolve_storage_path returned None or the path does not exist (.exists() is False). The row exists but the underlying uploaded WAV is missing, so streaming is impossible. Note this endpoint uses .exists() rather than .is_file(), so a directory at that path would also fail.","triggerScenarios":"GET /captures/{capture_id}/audio where row.audio_path is null, unresolvable under STORAGE_DIR, or the file was deleted/moved after the capture was created.","commonSituations":"Capture cleanup job deleted audio files but kept rows; storage volume unmounted; STORAGE_DIR reconfigured; the create flow wrote the DB row before the audio finished flushing.","solutions":["Verify config.resolve_storage_path(row.audio_path) returns a real path and STORAGE_DIR is correct.","Check the file exists on disk; if removed, the audio is unrecoverable — re-capture.","Make capture creation atomic: commit the row only after the audio write is fsynced and verified.","Audit cleanup logic to delete rows together with their audio files."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const cap = await getCapture(captureId);\nif (!cap || !cap.audio_path) {\n  warn('Capture audio may be missing on the server');\n}","typeGuard":"function captureHasArtifact(c: { audio_path: string | null } | null): boolean {\n  return c !== null && typeof c.audio_path === 'string' && c.audio_path.length > 0;\n}","tryCatchPattern":"try {\n  const r = await fetch(`/captures/${id}/audio`);\n  if (r.status === 404 && (await r.json()).detail === 'Audio file not found') {\n    markCaptureAudioCorrupt(id); // file gone, row remains\n    return;\n  }\n  play(await r.blob());\n} catch (e) { showNetworkError(e); }","preventionTips":["Write and fsync the audio file before committing the capture row.","Never delete capture audio files without also removing their rows.","Keep STORAGE_DIR stable; audit cleanup jobs for orphaned rows."],"tags":["capture","audio","storage","data-integrity","not-found","fastapi"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}