jamiepine/voicebox · error · HTTPException
{e}
Error message
{e} What it means
Returned as HTTP 400 by POST /generate. The route resolves an engine via _resolve_generation_engine (request.engine, else profile.default_engine, else profile.preset_engine, else 'qwen') and calls profiles.validate_profile_engine(profile, engine). That function raises ValueError for: a preset profile missing preset_engine/preset_voice_id, a preset profile whose preset_engine != engine, a designed profile missing design_prompt, or a cloned profile whose engine is not in CLONING_ENGINES. str(e) is surfaced as the detail.
Source
Thrown at backend/routes/generations.py:75
async def generate_speech(
data: models.GenerationRequest,
db: Session = Depends(get_db),
):
"""Generate speech from text using a voice profile."""
task_manager = get_task_manager()
generation_id = str(uuid.uuid4())
profile = await profiles.get_profile(data.profile_id, db)
if not profile:
raise HTTPException(status_code=404, detail="Profile not found")
from ..backends import engine_has_model_sizes
engine = _resolve_generation_engine(data, profile)
try:
profiles.validate_profile_engine(profile, engine)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
model_size = (data.model_size or "1.7B") if engine_has_model_sizes(engine) else None
text = data.text
source = "manual"
if data.personality and getattr(profile, "personality", None):
try:
llm_result = await personality.rewrite_as_profile(profile.personality, data.text)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
text = llm_result.text.strip()
if not text:
raise HTTPException(status_code=500, detail="LLM produced empty output; nothing to speak.")
source = "personality_speak"
generation = await history.create_generation(
profile_id=data.profile_id,
text=text,View on GitHub (pinned to 51f49dea19)
Solutions
- Read the detail string — it states the exact mismatch (expected vs requested engine, or missing metadata).
- Do not override engine for preset profiles; let _resolve_generation_engine fall back to profile.preset_engine.
- For cloned profiles, restrict the engine picker to entries in CLONING_ENGINES for the installed backend.
- If metadata is missing, repair the profile (re-set preset_engine/preset_voice_id or design_prompt) via the profile update endpoint.
Example fix
// before: client forces an incompatible engine
post('/generate', { profile_id, text, engine: 'kokoro' });
// after: respect the profile's engine constraints
const engine = profile.voice_type === 'preset' ? profile.preset_engine : undefined;
post('/generate', { profile_id, text, engine }); Defensive patterns
Strategy: try-catch
Validate before calling
# Validate engine compatibility before the request.
from backend.services import profiles as profiles_mod
try:
profiles_mod.validate_profile_engine(profile, resolved_engine)
except ValueError as e:
raise BadEngine(str(e)) # surface before the HTTP call Type guard
def engine_compatible_with_profile(profile, engine: str) -> bool:
try:
from backend.services.profiles import validate_profile_engine
validate_profile_engine(profile, engine)
return True
except ValueError:
return False Try / catch
try:
client.post('/generate', json=payload)
except HTTPStatusError as e:
if e.response.status_code == 400:
# drop the engine override and retry with the profile's default
payload.pop('engine', None)
client.post('/generate', json=payload)
return
raise Prevention
- Do not override engine for preset profiles; let the backend resolve preset_engine.
- Restrict the engine picker to CLONING_ENGINES for cloned profiles.
- Keep client and backend versions in sync when engine names change.
- Read the detail string — it names the expected vs requested engine.
When it happens
Trigger: Sending engine='kokoro' for a preset profile pinned to a different engine; requesting an engine that does not support cloned voices on a cloned profile; a designed profile whose design_prompt was wiped; a preset profile with corrupt/missing preset_engine metadata; explicit engine override that conflicts with the profile type.
Common situations: Frontend lets users pick any engine regardless of profile type; a profile was migrated/edited and its engine metadata was lost; engine name typo (e.g. 'chatterbox-turbo' vs 'chatterbox_turbo'); version skew where a new engine name is not in the installed CLONING_ENGINES list.
Related errors
- {exception message from delete_preset (ValueError)}
- Profile not found
- Only failed generations can be retried
- Generation must be completed to regenerate
- {error}
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/dbbf649aa0c6b9b1.
Report an issue: GitHub.