{"record":{"id":"661ceed16ee9f609","repo":"jamiepine/voicebox","slug":"designed-profiles-require-a-design-prompt","errorCode":null,"errorMessage":"Designed profiles require a design_prompt","messagePattern":"Designed profiles require a design_prompt","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"backend/services/profiles.py","lineNumber":173,"sourceCode":"    existing_profile = db.query(DBVoiceProfile).filter_by(name=data.name).first()\n    if existing_profile:\n        raise ValueError(f\"A profile with the name '{data.name}' already exists. Please choose a different name.\")\n\n    # Auto-set default_engine for preset profiles\n    default_engine = data.default_engine\n    voice_type = data.voice_type or \"cloned\"\n    if voice_type == \"preset\" and data.preset_engine and not default_engine:\n        default_engine = data.preset_engine\n\n    validation_error = _validate_profile_fields(\n        voice_type=voice_type,\n        preset_engine=data.preset_engine,\n        preset_voice_id=data.preset_voice_id,\n        design_prompt=data.design_prompt,\n        default_engine=default_engine,\n    )\n    if validation_error:\n        raise ValueError(validation_error)\n\n    db_profile = DBVoiceProfile(\n        id=str(uuid.uuid4()),\n        name=data.name,\n        description=data.description,\n        language=data.language,\n        voice_type=voice_type,\n        preset_engine=data.preset_engine,\n        preset_voice_id=data.preset_voice_id,\n        design_prompt=data.design_prompt,\n        default_engine=default_engine,\n        personality=data.personality,\n        created_at=datetime.utcnow(),\n        updated_at=datetime.utcnow(),\n    )\n\n    db.add(db_profile)\n    db.commit()","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/services/profiles.py#L155-L191","documentation":"Returned by _validate_profile_fields when voice_type=='designed' and design_prompt is missing or whitespace-only. A designed profile is defined by its text design_prompt, so an empty one leaves nothing to synthesize from.","triggerScenarios":"POSTing {voice_type:'designed'} with design_prompt omitted, null, empty string, or only whitespace.","commonSituations":"Client lets the user pick 'designed' but the prompt textarea is empty; trimming the input to empty before submit; field renamed in the client so design_prompt never gets populated.","solutions":["Provide a non-empty design_prompt (e.g. 'warm female voice, calm pacing, slight British accent').","Require a non-empty prompt in the UI before enabling submit when voice_type is 'designed'.","If you have no design prompt, switch voice_type to 'cloned' or 'preset' instead."],"exampleFix":"// before\n{\"name\":\"Narrator\",\"voice_type\":\"designed\",\"design_prompt\":\"   \"}\n// after\n{\"name\":\"Narrator\",\"voice_type\":\"designed\",\"design_prompt\":\"warm baritone, measured pace, neutral American accent\"}","handlingStrategy":"validation","validationCode":"def has_design_prompt(data) -> bool:\n    return bool(data.design_prompt and data.design_prompt.strip())\n\nif data.voice_type == \"designed\" and not has_design_prompt(data):\n    raise HTTPException(400, \"designed profiles require a non-empty design_prompt\")","typeGuard":"def is_complete_designed(data) -> bool:\n    p = getattr(data, \"design_prompt\", None)\n    return getattr(data, \"voice_type\", None) == \"designed\" and bool(p and p.strip())","tryCatchPattern":null,"preventionTips":["Require a non-empty prompt in the UI before allowing 'designed' submit.","Trim and check length client-side (max 2000 chars per the model).","Default voice_type to 'cloned' so an empty prompt doesn't accidentally pair with 'designed'."],"tags":["profiles","designed","validation","create","required-field"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}