jamiepine/voicebox · error · HTTPException
{validation error from validate_effects_chain}
Error message
{validation error from validate_effects_chain} What it means
HTTP 400 raised by POST /effects/preview/{generation_id} when validate_effects_chain() returns a non-None error string. The validator checks: effects_chain is a list; each entry is a dict; the 'type' is in EFFECT_REGISTRY; 'params' is a dict; each param key is known; each param value is numeric and within [min, max]. The returned string identifies the first violation (e.g. unknown effect type, out-of-range param) and is passed verbatim as the 400 detail.
Source
Thrown at backend/routes/effects.py:38
generation_id: str,
data: models.ApplyEffectsRequest,
db: Session = Depends(get_db),
):
"""Apply effects to a generation's clean audio and stream back without saving."""
gen = db.query(DBGeneration).filter_by(id=generation_id).first()
if not gen:
raise HTTPException(status_code=404, detail="Generation not found")
if (gen.status or "completed") != "completed":
raise HTTPException(status_code=400, detail="Generation is not completed")
from ..services import versions as versions_mod
from ..utils.effects import apply_effects, validate_effects_chain
from ..utils.audio import load_audio
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)
all_versions = versions_mod.list_versions(generation_id, db)
clean_version = next((v for v in all_versions if v.effects_chain is None), None)
source_path = clean_version.audio_path if clean_version else gen.audio_path
resolved_source_path = config.resolve_storage_path(source_path)
if resolved_source_path is None or not resolved_source_path.exists():
raise HTTPException(status_code=404, detail="Source audio file not found")
audio, sample_rate = await asyncio.to_thread(load_audio, str(resolved_source_path))
processed = await asyncio.to_thread(apply_effects, audio, sample_rate, chain_dicts)
import soundfile as sf
buf = io.BytesIO()
await asyncio.to_thread(lambda: sf.write(buf, processed, sample_rate, format="WAV"))
buf.seek(0)
return StreamingResponse(View on GitHub (pinned to 51f49dea19)
Solutions
- Read the detail string: it names the effect index, the param, and the allowed range.
- Cross-check effect types and param names against the EFFECT_REGISTRY (use the presets list or docs).
- Clamp numeric params to the registry min/max before submitting.
- Validate client-side with the same rules: list of dicts, known type, numeric in-range params.
Example fix
// before
const chain = [{ type: 'reverb', params: { room_size: 5.0 } }]; // out of range
// after
const chain = [{ type: 'reverb', params: { room_size: Math.min(1.0, Math.max(0.0, val)) } }];
await fetch(`/effects/preview/${genId}`, { method:'POST', body: JSON.stringify({ effects_chain: chain }) }); Defensive patterns
Strategy: validation
Validate before calling
function validateChain(chain, registry) {
if (!Array.isArray(chain)) return 'must be a list';
for (let i = 0; i < chain.length; i++) {
const e = chain[i];
if (!registry[e.type]) return `unknown type at ${i}`;
for (const [k, v] of Object.entries(e.params || {})) {
const def = registry[e.type].params[k];
if (!def) return `unknown param ${k}`;
if (typeof v !== 'number') return `${k} must be number`;
if (v < def.min || v > def.max) return `${k} out of range`;
}
}
return null;
}
const err = validateChain(chain, EFFECT_REGISTRY); if (err) throw new Error(err); Type guard
function isValidEffect(e, registry): e is { type: string; params: Record<string, number> } {
return !!registry[e?.type];
} Try / catch
const r = await fetch(`/effects/preview/${id}`, { method:'POST', body: JSON.stringify(data) });
if (r.status === 400) { const { detail } = await r.json(); /* validator message names effect index + param */ } Prevention
- Mirror validate_effects_chain rules on the client.
- Clamp numeric params to registry min/max.
- Use effect types and param names exactly as in EFFECT_REGISTRY.
When it happens
Trigger: Submitting an effects_chain with an unknown effect type, an unknown parameter name, a non-numeric parameter, or a numeric parameter outside its declared min/max range.
Common situations: Hand-building a chain with a typo'd effect name; using a param valid in an older version that was renamed/removed; sending string values for numeric params; exceeding a gain/frequency bound.
Related errors
- Generation is not completed
- {exception message from create_preset (ValueError)}
- {exception message from update_preset (ValueError)}
- {error}
- {exception message from update_channel (ValueError)}
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/36a578fe3349e707.
Report an issue: GitHub.