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

  1. Source source_version_id only from a fresh GET /generations/{generation_id}/versions response.
  2. On 404, re-fetch the versions list and repopulate the picker.
  3. Omit source_version_id entirely to let the backend pick the clean version (effects_chain is None) or fall back to gen.audio_path.
  4. 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

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


AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12). Data as JSON: /api/errors/b66a483ac5e3376e. Report an issue: GitHub.