jamiepine/voicebox · error · HTTPException
Source version not found
Error message
Source version not found
What it means
Returned as HTTP 404 by POST /generations/{generation_id}/versions/apply-effects. When the request supplies source_version_id, the route looks it up via next((v for v in list_versions(generation_id) ...) ...). If no version with that id belongs to this generation, it raises the 404. The version must belong to the same generation (cross-generation version ids are rejected).
Source
Thrown at backend/routes/effects.py:186
raise HTTPException(status_code=404, detail="Generation not found")
if (gen.status or "completed") != "completed":
raise HTTPException(status_code=400, detail="Generation is not completed")
from ..services import versions as versions_mod
from ..utils.effects import apply_effects, validate_effects_chain
from ..utils.audio import load_audio, save_audio
chain_dicts = [e.model_dump() for e in data.effects_chain]
error = validate_effects_chain(chain_dicts)
if error:
raise HTTPException(status_code=400, detail=error)
all_versions = versions_mod.list_versions(generation_id, db)
source_version_id = data.source_version_id
if source_version_id:
source_version = next((v for v in all_versions if v.id == source_version_id), None)
if not source_version:
raise HTTPException(status_code=404, detail="Source version not found")
source_path = source_version.audio_path
else:
clean_version = next((v for v in all_versions if v.effects_chain is None), None)
if not clean_version:
source_path = gen.audio_path
else:
source_path = clean_version.audio_path
source_version_id = clean_version.id
resolved_source_path = config.resolve_storage_path(source_path)
if resolved_source_path is None or not resolved_source_path.exists():
raise HTTPException(status_code=404, detail="Source audio file not found")
audio, sample_rate = await asyncio.to_thread(load_audio, str(resolved_source_path))
processed_audio = await asyncio.to_thread(apply_effects, audio, sample_rate, chain_dicts)
version_id = str(uuid.uuid4())
processed_path = config.get_generations_dir() / f"{generation_id}_{version_id[:8]}.wav"View on GitHub (pinned to 51f49dea19)
Solutions
- Source source_version_id only from a fresh GET /generations/{generation_id}/versions response.
- On 404, re-fetch the versions list and repopulate the picker.
- Omit source_version_id entirely to let the backend pick the clean version (effects_chain is None) or fall back to gen.audio_path.
- Verify the id belongs to the current generation before submitting.
Example fix
// before
post(url, { source_version_id: rememberedId, effects_chain })
// after
const versions = await api.get(`/generations/${genId}/versions`);
const src = versions.find(v => v.id === rememberedId);
post(url, { source_version_id: src ? src.id : undefined, effects_chain }) Defensive patterns
Strategy: validation
Validate before calling
# Confirm source_version_id belongs to this generation first.
versions = versions_mod.list_versions(generation_id, db)
if data.source_version_id and not any(v.id == data.source_version_id for v in versions):
raise SourceVersionMissing(data.source_version_id)
# safe to POST Type guard
def source_version_in_generation(source_id: str, versions: list) -> bool:
return any(v.id == source_id for v in versions) Try / catch
try:
client.post(url, json=payload)
except HTTPStatusError as e:
if e.response.status_code == 404 and 'Source version' in e.response.json()['detail']:
versions = client.get(f'/generations/{gen_id}/versions').json()
payload['source_version_id'] = None # let backend pick the clean version
client.post(url, json=payload)
return
raise Prevention
- Source source_version_id from a fresh versions listing for the same generation.
- Omit source_version_id to let the backend choose the clean version automatically.
- On 404, re-fetch versions and repopulate the picker.
- Verify v.generation_id matches before submitting.
When it happens
Trigger: Passing a source_version_id that does not exist; passing a version id that belongs to a different generation; passing a version id that was deleted between page load and submit; sending a generation id where a version id is expected.
Common situations: UI dropdown of versions is stale after a delete/regenerate; client constructs the request with the wrong field; user picks a version from another generation in a mixed list; version id copy-paste error.
Related errors
- Version not found
- Generation not found
- Cannot delete the last remaining version
- Profile not found
- Story or generation not found
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/b66a483ac5e3376e.
Report an issue: GitHub.