jamiepine/voicebox · warning · HTTPException

{str(e)}

Error message

{str(e)}

What it means

400 from POST /profiles. profiles.create_profile raises ValueError for expected business-rule failures — most commonly 'A profile with the name ... already exists' (duplicate-name guard) and schema-level validation errors produced while building the profile. The route maps ValueError to HTTPException(400, str(e)), so the human-readable reason is forwarded.

Source

Thrown at backend/routes/profiles.py:34

from ..database import VoiceProfile as DBVoiceProfile, get_db
from ..services import channels, export_import, personality, profiles
from ..services.profiles import _profile_to_response

logger = logging.getLogger(__name__)

router = APIRouter()


@router.post("/profiles", response_model=models.VoiceProfileResponse)
async def create_profile(
    data: models.VoiceProfileCreate,
    db: Session = Depends(get_db),
):
    """Create a new voice profile."""
    try:
        return await profiles.create_profile(data, db)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


@router.get("/profiles", response_model=list[models.VoiceProfileResponse])
async def list_profiles(db: Session = Depends(get_db)):
    """List all voice profiles."""
    return await profiles.list_profiles(db)


@router.post("/profiles/import", response_model=models.VoiceProfileResponse)
async def import_profile(
    file: UploadFile = File(...),
    db: Session = Depends(get_db),
):
    """Import a voice profile from a ZIP archive."""
    MAX_FILE_SIZE = 100 * 1024 * 1024

View on GitHub (pinned to 51f49dea19)

Solutions

  1. Read detail — it names the exact violated rule (duplicate name, missing preset metadata, etc.).
  2. For duplicate names, choose a unique name or PUT /profiles/{id} to update the existing one instead.
  3. For voice_type='preset', include preset_engine and preset_voice_id; for 'designed', include design_prompt.
  4. GET /profiles to reconcile client state with the server before retrying.

Example fix

// before
POST /profiles {"name":"Morgan","voice_type":"preset"}  // missing preset fields
// after
POST /profiles {"name":"Morgan","voice_type":"preset","preset_engine":"kokoro","preset_voice_id":"af_heart"}
Defensive patterns

Strategy: validation

Validate before calling

async function createProfile(input) {
  const list = await (await fetch('/profiles')).json();
  if (list.some(p => p.name.toLowerCase() === input.name.toLowerCase())) {
    throw new Error(`A profile named '${input.name}' already exists`);
  }
  if (input.voice_type === 'preset' && (!input.preset_engine || !input.preset_voice_id)) {
    throw new Error('preset profiles require preset_engine and preset_voice_id');
  }
  if (input.voice_type === 'designed' && !input.design_prompt) {
    throw new Error('designed profiles require design_prompt');
  }
  return await fetch('/profiles', {method:'POST', body: JSON.stringify(input)});
}

Try / catch

try {
  await fetch('/profiles', {method:'POST', body: JSON.stringify(input)});
} catch (e) {
  if (e.response?.status === 400) {
    // detail explains the violated rule (duplicate name, missing preset fields)
    showUserError(e.response.detail);
  } else throw e;
}

Prevention

When it happens

Trigger: POST /profiles with a name that already exists; language/voice_type/preset fields that fail internal consistency checks (e.g. preset profile missing preset_engine, designed profile missing design_prompt, cloned profile on an engine that doesn't support cloning); reference-text or design_prompt validation failing inside create_profile.

Common situations: User submits the same profile name twice; client didn't refresh the profile list before creating; switching voice_type without supplying the required companion fields (preset_engine + preset_voice_id, or design_prompt); frontend default values violate a server-side rule.

Related errors


AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12). Data as JSON: /api/errors/1e9ec27f4f3b9695. Report an issue: GitHub.