jamiepine/voicebox · error · HTTPException
Story item not found or invalid trim values
Error message
Story item not found or invalid trim values
What it means
Returned by PUT /stories/{story_id}/items/{item_id}/trim when stories.trim_story_item returns None (HTTP 404). The None sentinel is overloaded: it covers both 'item not found' and 'invalid trim values' (e.g., trim points out of range or start >= end), because the service returns the same falsy signal for either condition.
Source
Thrown at backend/routes/stories.py:150
):
"""Move a story item (update position and/or track)."""
item = await stories.move_story_item(story_id, item_id, data, 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}/trim", response_model=models.StoryItemDetail)
async def trim_story_item(
story_id: str,
item_id: str,
data: models.StoryItemTrim,
db: Session = Depends(get_db),
):
"""Trim a story item."""
item = await stories.trim_story_item(story_id, item_id, data, db)
if item is None:
raise HTTPException(status_code=404, detail="Story item not found or invalid trim values")
return item
@router.put("/stories/{story_id}/items/{item_id}/volume", response_model=models.StoryItemDetail)
async def update_story_item_volume(
story_id: str,
item_id: str,
data: models.StoryItemVolumeUpdate,
db: Session = Depends(get_db),
):
"""Set a story item's per-clip volume (linear gain, 0.0–2.0)."""
item = await stories.update_story_item_volume(story_id, item_id, data, db)
if item is None:
raise HTTPException(status_code=404, detail="Story item not found")
return item
@router.post("/stories/{story_id}/items/{item_id}/split", response_model=list[models.StoryItemDetail])View on GitHub (pinned to 51f49dea19)
Solutions
- Pre-validate trim values: 0 <= start < end <= item.duration using the current item detail.
- Confirm item_id exists in the story before trimming.
- Refresh item detail after prior trims to get updated bounds.
- Clamp trim markers to [0, duration] and ensure start < end client-side.
Example fix
// before
await api.put(`/stories/${sid}/items/${iid}/trim`, { start, end });
// after
const item = await getItem(sid, iid);
if (!item) throw new NotFound();
const s = clamp(start, 0, item.duration);
const e = clamp(end, 0, item.duration);
if (s >= e) throw new UserError('start must be < end');
await api.put(`/stories/${sid}/items/${iid}/trim`, { start: s, end: e }); Defensive patterns
Strategy: validation
Validate before calling
function validateTrim(item, start, end) {
if (!item) throw new Error('item not found');
const s = Math.max(0, Math.min(start, item.duration));
const e = Math.max(0, Math.min(end, item.duration));
if (s >= e) throw new Error('start must be < end');
return { start: s, end: e };
} Type guard
function isTrimPayload(v) { return !!v && typeof v.start === 'number' && typeof v.end === 'number'; } Try / catch
try { await api.put(`/stories/${sid}/items/${iid}/trim`, payload); }
catch (e) { if (e.response?.status === 404) { await refreshItem(sid, iid); showUser('item missing or trim invalid'); } throw e; } Prevention
- Pre-validate 0 <= start < end <= item.duration using current item detail.
- Refresh item detail after prior trims to get updated bounds.
- Clamp markers client-side and prevent start >= end.
When it happens
Trigger: Trimming a non-existent item, or supplying trim start/end values that are negative, exceed clip duration, or where start >= end. After a prior trim, the item's effective bounds may have changed, making the new values invalid.
Common situations: Editor computes trim points against stale clip duration. item_id stale after split. Trim UI allowing start >= end or out-of-bounds markers.
Related errors
- Story item not found
- {str(e)}
- Story not found
- Story or generation not found
- Invalid timecode update request
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/3c8611d4c42b0438.
Report an issue: GitHub.