jamiepine/voicebox · error · HTTPException

{error}

Error message

{error}

What it means

Returned by PUT /profiles/{profile_id}/effects when validate_effects_chain() rejects the supplied chain (HTTP 400). The handler serializes each effect via model_dump, then delegates to validate_effects_chain; only if that returns a non-empty error string does it raise, using the validator's message verbatim. It means the request body parsed as ProfileEffectsUpdate but the chain contents are semantically invalid.

Source

Thrown at backend/routes/profiles.py:354

@router.put("/profiles/{profile_id}/effects", response_model=models.VoiceProfileResponse)
async def update_profile_effects(
    profile_id: str,
    data: models.ProfileEffectsUpdate,
    db: Session = Depends(get_db),
):
    """Set or clear the default effects chain for a voice profile."""
    profile = db.query(DBVoiceProfile).filter_by(id=profile_id).first()
    if not profile:
        raise HTTPException(status_code=404, detail="Profile not found")

    if data.effects_chain is not None:
        from ..utils.effects import validate_effects_chain

        chain_dicts = [e.model_dump() for e in data.effects_chain]
        error = validate_effects_chain(chain_dicts)
        if error:
            raise HTTPException(status_code=400, detail=error)
        profile.effects_chain = _json.dumps(chain_dicts)
    else:
        profile.effects_chain = None

    profile.updated_at = datetime.utcnow()
    db.commit()
    db.refresh(profile)

    return _profile_to_response(profile)


# ── Personality endpoint ──────────────────────────────────────────────
# Only ``/profiles/{id}/compose`` remains — the UI's compose button
# produces a fresh in-character utterance the user can edit before
# speaking. Rewrite now happens inside ``/generate`` (and ``/speak``)
# when ``personality=true``; there is no standalone rewrite/respond/speak
# endpoint.

View on GitHub (pinned to 51f49dea19)

Solutions

  1. Inspect the exact detail string from the 400 response — it comes straight from validate_effects_chain and names the offending field/effect.
  2. Cross-check the effect names and parameter keys against the current backend's supported effects list.
  3. Validate the chain client-side against the same schema before submitting.
  4. Ensure each effect object includes all required keys and that numeric params are within documented ranges.

Example fix

// before
await api.put(`/profiles/${id}/effects`, { effects_chain: rawChain });

// after
const errs = validateChainClient(rawChain); // mirror of validate_effects_chain
if (errs.length) throw new UserError(errs);
await api.put(`/profiles/${id}/effects`, { effects_chain: rawChain });
Defensive patterns

Strategy: validation

Validate before calling

function validateChainClient(chain) {
  const supported = new Set(['eq','compressor','reverb','gain']);
  const errs = [];
  for (const e of chain) {
    if (!supported.has(e.type)) errs.push(`unknown effect: ${e.type}`);
    if (typeof e.params?.gain === 'number' && (e.params.gain < 0 || e.params.gain > 4))
      errs.push(`gain out of range on ${e.type}`);
  }
  return errs;
}

Type guard

function isEffectsChain(v) { return Array.isArray(v) && v.every(e => e && typeof e === 'object' && typeof e.type === 'string'); }

Try / catch

try { await api.put(`/profiles/${id}/effects`, { effects_chain: chain }); }
catch (e) { if (e.response?.status === 400) showUser(e.response.data.detail); throw e; }

Prevention

When it happens

Trigger: Submitting an effects_chain with an unknown effect type, missing required parameters for an effect, out-of-range values, or a malformed effect object. Passing an empty object instead of a fully-formed effect descriptor.

Common situations: Frontend builds the chain from a stale list of supported effects after an engine upgrade that renamed/removed an effect. Copy-pasting a chain from docs for a different backend version. Negative or NaN gain values, unsupported sample-rate-dependent parameters.

Related errors


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