jamiepine/voicebox · error · HTTPException
Invalid timecode update request
Error message
Invalid timecode update request
What it means
Returned by PUT /stories/{story_id}/items/times when stories.update_story_item_times returns falsy (HTTP 400, not 404). The handler treats a falsy update as a malformed/invalid request rather than a not-found, signaling that the StoryItemBatchUpdate payload failed validation or referenced items in an inconsistent way.
Source
Thrown at backend/routes/stories.py:107
db: Session = Depends(get_db),
):
"""Remove a story item from a story."""
success = await stories.remove_item_from_story(story_id, item_id, db)
if not success:
raise HTTPException(status_code=404, detail="Story item not found")
return {"message": "Item removed successfully"}
@router.put("/stories/{story_id}/items/times")
async def update_story_item_times(
story_id: str,
data: models.StoryItemBatchUpdate,
db: Session = Depends(get_db),
):
"""Update story item timecodes."""
success = await stories.update_story_item_times(story_id, data, db)
if not success:
raise HTTPException(status_code=400, detail="Invalid timecode update request")
return {"message": "Item timecodes updated successfully"}
@router.put("/stories/{story_id}/items/reorder", response_model=list[models.StoryItemDetail])
async def reorder_story_items(
story_id: str,
data: models.StoryItemReorder,
db: Session = Depends(get_db),
):
"""Reorder story items and recalculate timecodes."""
items = await stories.reorder_story_items(story_id, data.generation_ids, db)
if items is None:
raise HTTPException(
status_code=400, detail="Invalid reorder request - ensure all generation IDs belong to this story"
)
return items
View on GitHub (pinned to 51f49dea19)
Solutions
- Validate that every item_id in the batch belongs to the story (via the detail endpoint) before submitting.
- Ensure timecodes are non-negative, ordered, and within each clip's bounds.
- Reject empty batches client-side.
- Re-fetch story detail after concurrent edits and rebuild the batch.
Example fix
// before
await api.put(`/stories/${sid}/items/times`, batch);
// after
if (!batch.length) throw new UserError('empty batch');
ensureTimesValid(batch, storyDetail);
await api.put(`/stories/${sid}/items/times`, batch); Defensive patterns
Strategy: validation
Validate before calling
function validateTimeBatch(batch, storyItems) {
if (!Array.isArray(batch) || !batch.length) throw new Error('empty batch');
const known = new Set(storyItems.map(i => i.id));
for (const b of batch) {
if (!known.has(b.item_id)) throw new Error(`unknown item ${b.item_id}`);
if (b.start < 0 || b.end < 0 || b.start >= b.end) throw new Error(`bad timecodes for ${b.item_id}`);
}
} Type guard
function isTimeBatch(v) { return Array.isArray(v) && v.every(b => typeof b.item_id === 'string' && typeof b.start === 'number' && typeof b.end === 'number'); } Try / catch
try { await api.put(`/stories/${sid}/items/times`, batch); }
catch (e) { if (e.response?.status === 400) { await refreshStory(sid); showUser(e.response.data.detail); } throw e; } Prevention
- Ensure every item_id in the batch belongs to the story.
- Validate timecodes are non-negative, ordered, and within clip bounds.
- Reject empty batches client-side.
- Rebuild the batch after concurrent edits.
When it happens
Trigger: Submitting timecode updates where the referenced item ids don't match the story, timecodes are negative/out-of-order, or the batch is empty/malformed. The service returns falsy for any precondition failure during the batch update.
Common situations: Editor sends a batch with item ids from a different story. Timecodes computed client-side exceed clip duration. Empty batch submitted on save. Concurrent edits invalidate the batch.
Related errors
- {str(e)}
- Invalid reorder request - ensure all generation IDs belong t
- Story item not found or invalid trim values
- {error}
- No voice profile resolved. Pass `profile` (name or id), or c
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/02516b0dd06408bc.
Report an issue: GitHub.