jamiepine/voicebox · error · HTTPException

Version not found

Error message

Version not found

What it means

Returned as HTTP 404 by the set-default-version endpoint for /generations/{generation_id}/versions/{version_id}. The route fetches the version via versions.get_version(version_id) and additionally requires version.generation_id == generation_id. If the version does not exist, or it exists but belongs to a different generation, this 404 is raised. The mismatch check prevents cross-generation default promotion.

Source

Thrown at backend/routes/effects.py:236

    return version


@router.put(
    "/generations/{generation_id}/versions/{version_id}/set-default",
    response_model=models.GenerationVersionResponse,
)
async def set_default_version(
    generation_id: str,
    version_id: str,
    db: Session = Depends(get_db),
):
    """Set a specific version as the default for a generation."""
    from ..services import versions as versions_mod

    version = versions_mod.get_version(version_id, db)
    if not version or version.generation_id != generation_id:
        raise HTTPException(status_code=404, detail="Version not found")

    result = versions_mod.set_default_version(version_id, db)
    if not result:
        raise HTTPException(status_code=404, detail="Version not found")
    return result


@router.delete("/generations/{generation_id}/versions/{version_id}")
async def delete_generation_version(
    generation_id: str,
    version_id: str,
    db: Session = Depends(get_db),
):
    """Delete a version. Cannot delete the last remaining version."""
    from ..services import versions as versions_mod

    version = versions_mod.get_version(version_id, db)
    if not version or version.generation_id != generation_id:

View on GitHub (pinned to 51f49dea19)

Solutions

  1. Source version_id and generation_id from a fresh GET /generations/{generation_id}/versions response.
  2. On 404, re-fetch the versions list and re-render the picker.
  3. Ensure the version's generation_id matches the path generation_id client-side before the call.
  4. Double-check URL template ordering: /generations/{generation_id}/versions/{version_id}.

Example fix

// before
api.post(`/generations/${genId}/versions/${cachedVersionId}/default`)
// after
const versions = await api.get(`/generations/${genId}/versions`);
const v = versions.find(x => x.id === cachedVersionId);
if (!v || v.generation_id !== genId) return;
api.post(`/generations/${genId}/versions/${v.id}/default`)
Defensive patterns

Strategy: validation

Validate before calling

v = versions_mod.get_version(version_id, db)
if v is None or v.generation_id != generation_id:
    raise VersionNotInGeneration(version_id, generation_id)
# safe to call set-default-version

Type guard

def version_belongs_to_generation(version, generation_id: str) -> bool:
    return version is not None and version.generation_id == generation_id

Try / catch

try:
    client.post(f'/generations/{gen_id}/versions/{ver_id}/default')
except HTTPStatusError as e:
    if e.response.status_code == 404:
        versions = client.get(f'/generations/{gen_id}/versions').json()
        repopulate_picker(versions)
        return
    raise

Prevention

When it happens

Trigger: Passing a version_id that does not exist; passing a version id from a different generation; the version was deleted between page load and the set-default call; swapping generation_id and version_id in the URL.

Common situations: Stale version list in the UI after a delete; user has multiple generations open and the wrong one's version is set as default; client reuses a cached version id after regeneration created new versions; URL path argument ordering mistake.

Related errors


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