{"record":{"id":"ba9580a93cc7598d","repo":"jamiepine/voicebox","slug":"preset-profile-profile-id-is-missing-preset-engi-ba9580","errorCode":null,"errorMessage":"Preset profile {profile_id} is missing preset engine metadata","messagePattern":"Preset profile (.+?) is missing preset engine metadata","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"backend/services/profiles.py","lineNumber":550,"sourceCode":"        use_cache: Whether to use cached prompts\n        engine: TTS engine to create prompt for\n\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        }","sourceCodeStart":532,"sourceCodeEnd":568,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/services/profiles.py#L532-L568","documentation":"Raised inside the preset branch of create_voice_prompt_from_profile() when a profile whose voice_type == 'preset' lacks either preset_engine or preset_voice_id. A preset profile is meant to reference a built-in engine voice, so both metadata fields are mandatory. This is a data-integrity error: the profile row is malformed and cannot be used to synthesize speech.","triggerScenarios":"A preset profile row was inserted with NULL preset_engine or preset_voice_id; an ORM update cleared one field; a manual DB edit populated voice_type='preset' without the supporting metadata; a migration populated preset profiles incompletely.","commonSituations":"Seeding scripts that set voice_type but skip metadata; partial writes during profile creation; schema changes that added preset_engine/preset_voice_id columns after existing rows were created; manual SQL inserts used to bootstrap test data.","solutions":["Backfill the missing fields on the profile row: UPDATE voice_profiles SET preset_engine=..., preset_voice_id=... WHERE id=....","Recreate the preset profile through the normal create_profile flow so all required fields are set.","If the row is genuinely invalid, delete it and let it be re-seeded.","Add a NOT NULL constraint (and an app-layer validator) on preset_engine/preset_voice_id when voice_type='preset' to prevent recurrence."],"exampleFix":"// before: profile row has voice_type='preset', preset_engine=NULL\n// after: ensure both metadata fields are populated\nprofile.preset_engine = 'qwen'\nprofile.preset_voice_id = 'cherry'\ndb.commit()","handlingStrategy":"validation","validationCode":"from backend.models import DBVoiceProfile\n\ndef is_preset_profile_complete(profile) -> bool:\n    if (profile.voice_type or 'cloned') != 'preset':\n        return True\n    return bool(profile.preset_engine and profile.preset_voice_id)\n\nprofile = db.query(DBVoiceProfile).filter_by(id=profile_id).first()\nif not is_preset_profile_complete(profile):\n    raise HTTPException(409, 'Preset profile is missing engine metadata')","typeGuard":null,"tryCatchPattern":"try:\n    prompt = await create_voice_prompt_from_profile(profile_id, db, engine=engine)\nexcept ValueError as e:\n    if 'missing preset engine metadata' in str(e):\n        # flag for data backfill / re-seed\n        log_data_integrity(profile_id)\n        raise HTTPException(409, str(e))\n    raise","preventionTips":["Enforce preset_engine/preset_voice_id NOT NULL when voice_type='preset' at the schema level.","Validate preset profile completeness in the create_profile/update_profile path.","Add an integration test that asserts seeded preset profiles carry full metadata."],"tags":["profiles","data-integrity","preset","voicebox"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}