jamiepine/voicebox · error · HTTPException
Generation must be completed to regenerate
Error message
Generation must be completed to regenerate
What it means
Returned as HTTP 400 by POST /generate/{generation_id}/regenerate. The guard `(gen.status or 'completed') != 'completed'` only permits regenerate when status is 'completed' (NULL also defaults to 'completed' and is allowed). Regenerate needs finished audio to re-run and produce a new version; 'failed' or 'generating' rows are rejected.
Source
Thrown at backend/routes/generations.py:200
instruct=gen.instruct,
mode="retry",
)
)
return models.GenerationResponse.model_validate(gen)
@router.post(
"/generate/{generation_id}/regenerate",
response_model=models.GenerationResponse,
)
async def regenerate_generation(generation_id: str, db: Session = Depends(get_db)):
"""Re-run TTS with the same parameters and save the result as a new version."""
gen = db.query(DBGeneration).filter_by(id=generation_id).first()
if not gen:
raise HTTPException(status_code=404, detail="Generation not found")
if (gen.status or "completed") != "completed":
raise HTTPException(status_code=400, detail="Generation must be completed to regenerate")
gen.status = "generating"
gen.error = None
db.commit()
db.refresh(gen)
task_manager = get_task_manager()
task_manager.start_generation(
task_id=generation_id,
profile_id=gen.profile_id,
text=gen.text,
)
version_id = str(uuid.uuid4())
enqueue_generation(
generation_id,
run_generation(View on GitHub (pinned to 51f49dea19)
Solutions
- Only show 'regenerate' when generation.status === 'completed' (or NULL treated as completed); show 'retry' for 'failed'.
- After a regenerate call, optimistically set status to 'generating' and disable the button until completion.
- Refresh status before the call to catch rows that changed state since page load.
- For 'failed' rows, route the user to POST /generate/{id}/retry instead.
Example fix
// before
regenBtn.onclick = () => post(`/generate/${id}/regenerate`);
// after
const done = generation.status === 'completed' || generation.status == null;
regenBtn.disabled = !done;
regenBtn.onclick = async () => {
await post(`/generate/${id}/regenerate`);
generation.status = 'generating';
regenBtn.disabled = true;
}; Defensive patterns
Strategy: validation
Validate before calling
status = (gen.status or 'completed')
if status != 'completed':
raise NotRegeneratable(status) # would 400
# safe to POST /generate/{id}/regenerate Type guard
def is_regeneratable(gen) -> bool:
return (getattr(gen, 'status', None) or 'completed') == 'completed' Try / catch
try:
client.post(f'/generate/{generation_id}/regenerate')
except HTTPStatusError as e:
if e.response.status_code == 400 and 'completed' in e.response.json()['detail']:
if current_status(generation_id) == 'failed':
client.post(f'/generate/{generation_id}/retry') # route failed -> retry
else:
wait_for_status(generation_id, 'completed')
return
raise Prevention
- Only show regenerate for status === 'completed' (or NULL).
- Route 'failed' rows to retry, not regenerate.
- After a regenerate call, set status 'generating' and disable the button until done.
- Refresh status before the call to catch state changes.
When it happens
Trigger: Regenerating a 'failed' generation (use retry instead); regenerating a 'generating' generation still in flight; regenerating a row with an unexpected custom status value.
Common situations: User clicks regenerate on a failed generation instead of retry; double-submit where the first call moved status to 'generating'; custom workflow statuses injected outside the normal state machine; UI showing regenerate for non-completed rows.
Related errors
- Only failed generations can be retried
- {e}
- {exception message from delete_preset (ValueError)}
- Cannot delete the last remaining version
- Profile not found
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/5587fedb52cafcc5.
Report an issue: GitHub.