jamiepine/voicebox · error · HTTPException
Audio file not found
Error message
Audio file not found
What it means
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.
Source
Thrown at backend/routes/captures.py:102
@router.get("/captures/{capture_id}", response_model=models.CaptureResponse)
async def get_capture_endpoint(capture_id: str, db: Session = Depends(get_db)):
capture = captures_service.get_capture(capture_id, db)
if not capture:
raise HTTPException(status_code=404, detail="Capture not found")
return capture
@router.get("/captures/{capture_id}/audio")
async def get_capture_audio_endpoint(capture_id: str, db: Session = Depends(get_db)):
"""Stream the original capture audio file."""
row = db.query(DBCapture).filter(DBCapture.id == capture_id).first()
if not row:
raise HTTPException(status_code=404, detail="Capture not found")
audio_path = config.resolve_storage_path(row.audio_path)
if audio_path is None or not audio_path.exists():
raise HTTPException(status_code=404, detail="Audio file not found")
return FileResponse(
audio_path,
media_type="audio/wav",
filename=f"capture_{capture_id}.wav",
)
@router.delete("/captures/{capture_id}")
async def delete_capture_endpoint(capture_id: str, db: Session = Depends(get_db)):
deleted = captures_service.delete_capture(capture_id, db)
if not deleted:
raise HTTPException(status_code=404, detail="Capture not found")
return {"message": f"Capture {capture_id} deleted"}
@router.post("/captures/{capture_id}/refine", response_model=models.CaptureResponse)
async def refine_capture_endpoint(View on GitHub (pinned to 51f49dea19)
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.
Defensive patterns
Strategy: validation
Validate before calling
const cap = await getCapture(captureId);
if (!cap || !cap.audio_path) {
warn('Capture audio may be missing on the server');
} Type guard
function captureHasArtifact(c: { audio_path: string | null } | null): boolean {
return c !== null && typeof c.audio_path === 'string' && c.audio_path.length > 0;
} Try / catch
try {
const r = await fetch(`/captures/${id}/audio`);
if (r.status === 404 && (await r.json()).detail === 'Audio file not found') {
markCaptureAudioCorrupt(id); // file gone, row remains
return;
}
play(await r.blob());
} catch (e) { showNetworkError(e); } Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Generation failed; no audio available
- Audio file not found
- Sample not found
- Capture not found
- Source audio file not found
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/28c088e023abed20.
Report an issue: GitHub.