jamiepine/voicebox · error · HTTPException
Audio file not found
Error message
Audio file not found
What it means
Returned by GET /history/{generation_id}/export-audio when `config.resolve_storage_path(generation.audio_path)` returns None OR the resolved path does not satisfy `.is_file()`. This is the case where the DB row points at an audio file, but the file is absent from disk — an orphaned reference. HTTP 404.
Source
Thrown at backend/routes/history.py:180
)
@router.get("/history/{generation_id}/export-audio")
async def export_generation_audio(
generation_id: str,
db: Session = Depends(get_db),
):
"""Export only the audio file from a generation."""
generation = db.query(DBGeneration).filter_by(id=generation_id).first()
if not generation:
raise HTTPException(status_code=404, detail="Generation not found")
if not generation.audio_path:
raise HTTPException(status_code=404, detail="Generation has no audio file")
audio_path = config.resolve_storage_path(generation.audio_path)
if audio_path is None or not audio_path.is_file():
raise HTTPException(status_code=404, detail="Audio file not found")
safe_text = "".join(c for c in generation.text[:30] if c.isalnum() or c in (" ", "-", "_")).strip()
if not safe_text:
safe_text = "generation"
# Append a short id so exports of similarly-worded generations don't collide
# on the same filename (the first 30 chars are frequently identical).
filename = f"{safe_text}-{generation_id[:8]}.wav"
return FileResponse(
audio_path,
media_type="audio/wav",
headers={"Content-Disposition": safe_content_disposition("attachment", filename)},
)
View on GitHub (pinned to 51f49dea19)
Solutions
- Verify config.get_generations_dir() / the storage root points at the same volume the rows were written to.
- If audio was lost, delete the orphaned generation row or re-generate it.
- Run a consistency sweep: for each row with audio_path, confirm the file exists; clean orphans.
- Keep the storage root stable across app updates to avoid path resolution failures.
Example fix
// before: assume file exists
window.open(`/history/${id}/export-audio`);
// after: health-check storage on startup, surface orphan rows
for (const row of history) {
if (row.audio_path && !await fileExists(row.audio_path)) flagOrphan(row.id);
} Defensive patterns
Strategy: validation
Validate before calling
// Client cannot check disk; guard on status+path, and run server-side consistency checks if (!hasAudioPath(row)) return; // Server-side periodic job: // for row in generations: assert resolve_storage_path(row.audio_path).is_file()
Type guard
function pointsToExistingAudioSync(row, existsFn) {
return row != null && typeof row.audio_path === 'string' && existsFn(row.audio_path);
} Try / catch
try {
const res = await fetch(`/history/${id}/export-audio`);
if (res.status === 404) {
const d = await res.json();
if (/audio file not found/i.test(d.detail)) { flagOrphan(id); return; }
}
} catch (e) { console.error(e); } Prevention
- Keep the storage root path stable across updates.
- Run a consistency sweep to detect orphaned audio_path references.
- Never manually delete files from the generations directory without updating rows.
When it happens
Trigger: The audio file was deleted/moved from the generations directory while the DB row remains; the storage path config changed so resolve_storage_path can no longer map the stored relative path; the file is on an unmounted external volume.
Common situations: Manual cleanup of the generations dir; migrations that moved storage without updating rows; relative-path mismatch after the configured base dir changed; unmounted network storage.
Related errors
- Generation has no audio file
- Generation failed; no audio available
- Audio file not found
- Source audio file not found
- Generation not found
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/6405f111224e64ca.
Report an issue: GitHub.