{"record":{"id":"15712ce2b5de372e","repo":"jamiepine/voicebox","slug":"preset-profile-profile-id-only-supports-engine-15712c","errorCode":null,"errorMessage":"Preset profile {profile_id} only supports engine '{profile.preset_engine}', not '{engine}'","messagePattern":"Preset profile (.+?) only supports engine '(.+?)', not '(.+?)'","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"backend/services/profiles.py","lineNumber":552,"sourceCode":"\n    Returns:\n        Voice prompt dictionary\n    \"\"\"\n    from ..backends import get_tts_backend_for_engine\n\n    profile = db.query(DBVoiceProfile).filter_by(id=profile_id).first()\n    if not profile:\n        raise ValueError(f\"Profile not found: {profile_id}\")\n\n    voice_type = getattr(profile, \"voice_type\", None) or \"cloned\"\n    validate_profile_engine(profile, engine)\n\n    # ── Preset profiles: return engine-specific voice reference ──\n    if voice_type == \"preset\":\n        if not profile.preset_engine or not profile.preset_voice_id:\n            raise ValueError(f\"Preset profile {profile_id} is missing preset engine metadata\")\n        if profile.preset_engine != engine:\n            raise ValueError(\n                f\"Preset profile {profile_id} only supports engine '{profile.preset_engine}', not '{engine}'\"\n            )\n        return {\n            \"voice_type\": \"preset\",\n            \"preset_engine\": profile.preset_engine,\n            \"preset_voice_id\": profile.preset_voice_id,\n        }\n\n    # ── Designed profiles: return text description (future) ──\n    if voice_type == \"designed\":\n        if not profile.design_prompt or not profile.design_prompt.strip():\n            raise ValueError(f\"Designed profile {profile_id} is missing design_prompt\")\n        return {\n            \"voice_type\": \"designed\",\n            \"design_prompt\": profile.design_prompt,\n        }\n\n    if engine not in CLONING_ENGINES:","sourceCodeStart":534,"sourceCodeEnd":570,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/services/profiles.py#L534-L570","documentation":"Raised in the preset branch of create_voice_prompt_from_profile() when the requested engine argument differs from the profile's preset_engine. Preset voices are bound to one specific engine at creation time (e.g. a 'qwen' preset voice id is not valid for a 'chatterbox' backend), so the engine parameter must agree with profile.preset_engine.","triggerScenarios":"Calling create_voice_prompt_from_profile(profile_id, db, engine='chatterbox') on a profile whose preset_engine is 'qwen'; defaulting the engine parameter to the server default when the preset profile was created for a different engine; client requests synthesis with an engine that does not match the preset profile's bound engine.","commonSituations":"Server default engine changed after preset profiles were created; client UI lets the user pick an engine independently of the selected preset voice; mixing preset profiles across multi-engine deployments without reconciling the engine param.","solutions":["Pass engine=profile.preset_engine when calling the function for a preset profile instead of a hardcoded/default value.","In the API route, read the profile first and set the engine from profile.preset_engine before invoking synthesis.","Restrict the client engine selector so users cannot choose an engine incompatible with a selected preset voice.","If multi-engine support is required for the same voice, create separate preset profiles per engine."],"exampleFix":"// before\nprompt = await create_voice_prompt_from_profile(profile_id, db, engine='chatterbox')\n// after: derive engine from the preset profile itself\nprofile = db.query(DBVoiceProfile).filter_by(id=profile_id).first()\nprompt = await create_voice_prompt_from_profile(profile_id, db, engine=profile.preset_engine)","handlingStrategy":"validation","validationCode":"profile = db.query(DBVoiceProfile).filter_by(id=profile_id).first()\nif (profile.voice_type or 'cloned') == 'preset' and engine != profile.preset_engine:\n    raise HTTPException(409, f\"Engine {engine} incompatible with preset profile (use {profile.preset_engine})\")","typeGuard":null,"tryCatchPattern":"try:\n    prompt = await create_voice_prompt_from_profile(profile_id, db, engine=engine)\nexcept ValueError as e:\n    if 'only supports engine' in str(e):\n        raise HTTPException(409, str(e))\n    raise","preventionTips":["Derive the engine argument from profile.preset_engine rather than a global default.","Restrict the client engine selector based on the selected preset profile.","Create separate preset profiles per engine if a voice must work across engines."],"tags":["profiles","preset","engine-mismatch","voicebox"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}