jamiepine/voicebox · error · HTTPException
Story item or version not found
Error message
Story item or version not found
What it means
Raised (HTTP 404) by PUT /stories/{story_id}/items/{item_id}/version when stories.set_story_item_version returns None. The service returns None when the item is missing, its generation is missing, or the supplied version_id does not belong to the item's generation_id (DBGenerationVersion lookup filtered by both id and generation_id fails). The message covers all three cases.
Source
Thrown at backend/routes/stories.py:205
):
"""Duplicate a story item."""
item = await stories.duplicate_story_item(story_id, item_id, db)
if item is None:
raise HTTPException(status_code=404, detail="Story item not found")
return item
@router.put("/stories/{story_id}/items/{item_id}/version", response_model=models.StoryItemDetail)
async def set_story_item_version(
story_id: str,
item_id: str,
data: models.StoryItemVersionUpdate,
db: Session = Depends(get_db),
):
"""Pin a story item to a specific generation version."""
item = await stories.set_story_item_version(story_id, item_id, data, db)
if item is None:
raise HTTPException(status_code=404, detail="Story item or version not found")
return item
@router.get("/stories/{story_id}/export-audio")
async def export_story_audio(
story_id: str,
db: Session = Depends(get_db),
):
"""Export story as single mixed audio file."""
try:
story = db.query(database.Story).filter_by(id=story_id).first()
if not story:
raise HTTPException(status_code=404, detail="Story not found")
audio_bytes = await stories.export_story_audio(story_id, db)
if not audio_bytes:
raise HTTPException(status_code=400, detail="Story has no audio items")
View on GitHub (pinned to 51f49dea19)
Solutions
- Re-fetch the available versions for the item's current generation_id before pinning.
- To unpin, send version_id = null/None — that path skips the version lookup and only needs a valid item.
- Verify item_id belongs to story_id (GET /stories/{story_id}) if the 404 is unexpected.
Example fix
// before
await api.setItemVersion(storyId, itemId, { version_id: cachedVersionId });
// after
const versions = await api.listGenerationVersions(item.generation_id);
if (!versions.some(v => v.id === cachedVersionId)) {
throw new Error('version no longer available for this generation');
}
await api.setItemVersion(storyId, itemId, { version_id: cachedVersionId }); Defensive patterns
Strategy: validation
Validate before calling
async function pinVersionIfAvailable(api, storyId, itemId, generationId, versionId) {
if (versionId == null) {
return api.setItemVersion(storyId, itemId, { version_id: null }); // unpin always allowed
}
const versions = await api.listGenerationVersions(generationId);
if (!versions.some(v => v.id === versionId)) {
throw new Error('version not available for this generation');
}
return api.setItemVersion(storyId, itemId, { version_id: versionId });
} Type guard
function isPinnableVersion(versions, versionId, item) {
return Array.isArray(versions)
&& versions.some(v => v.id === versionId && v.generation_id === item.generation_id);
} Try / catch
try { await api.setItemVersion(storyId, itemId, payload); }
catch (e) {
if (e.status === 404) { await refreshItem(storyId, itemId); notify('Item or version not available'); }
else throw e;
} Prevention
- Re-fetch version options for the item's current generation before pinning.
- Send version_id=null to unpin — it skips the version lookup.
- Invalidate cached version ids whenever the generation is regenerated.
When it happens
Trigger: PUT .../version with a version_id from a different generation, a version_id that was deleted, an item_id/story_id mismatch, or pinning a version after the generation's version rows were regenerated.
Common situations: UI lists version options from one generation but the item was since re-pointed to another generation; client caches version_id across a regeneration that dropped old versions.
Related errors
- Story item not found or invalid trim values
- Story item not found or invalid split point
- Story has no audio items
- {exception message from set_channel_voices (ValueError)}
- Generation has no audio file
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/2d0566b77838b3a4.
Report an issue: GitHub.