{"record":{"id":"a0c0c6d8813edd63","repo":"jamiepine/voicebox","slug":"effect-effect-type-at-index-i-param-param","errorCode":null,"errorMessage":"Effect '{effect_type}' at index {i}: param '{param_name}' must be between {pdef['min']} and {pdef['max']} (got {value})","messagePattern":"Effect '(.+?)' at index (.+?): param '(.+?)' must be between (.+?) and (.+?) \\(got (.+?)\\)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"backend/services/effects.py","lineNumber":102,"sourceCode":"\ndef update_preset(preset_id: str, data: EffectPresetUpdate, db: Session) -> Optional[EffectPresetResponse]:\n    \"\"\"Update a user effect preset. Cannot modify built-in presets.\"\"\"\n    preset = db.query(DBEffectPreset).filter_by(id=preset_id).first()\n    if not preset:\n        return None\n    if preset.is_builtin:\n        raise ValueError(\"Cannot modify built-in presets\")\n\n    if data.name is not None:\n        preset.name = data.name\n    if data.description is not None:\n        preset.description = data.description\n    if data.effects_chain is not None:\n\n        chain_dicts = [e.model_dump() for e in data.effects_chain]\n        error = validate_effects_chain(chain_dicts)\n        if error:\n            raise ValueError(error)\n        preset.effects_chain = json.dumps(chain_dicts)\n\n    db.commit()\n    db.refresh(preset)\n    return _preset_response(preset)\n\n\ndef delete_preset(preset_id: str, db: Session) -> bool:\n    \"\"\"Delete a user effect preset. Cannot delete built-in presets.\"\"\"\n    preset = db.query(DBEffectPreset).filter_by(id=preset_id).first()\n    if not preset:\n        return False\n    if preset.is_builtin:\n        raise ValueError(\"Cannot delete built-in presets\")\n\n    db.delete(preset)\n    db.commit()\n    return True","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/services/effects.py#L84-L120","documentation":"validate_effects_chain() (backend/utils/effects.py) checks each numeric param against the min/max recorded in EFFECT_REGISTRY[type]['params'][name]. A value below min or above max returns this message, which create_preset()/update_preset() raise as ValueError. This keeps pedalboard construction inside valid DSP ranges before it reaches the DB or build_pedalboard().","triggerScenarios":"Saving a preset with e.g. gain_db=60 (max 40), pitch_shift semitones=-20 (min -12), chorus rate_hz=0 (min 0.01), or lowpass cutoff above 20000.","commonSituations":"Frontend slider bounds out of sync with the backend registry; manual JSON edit pushing an extreme value; stale cached effect schema on the client.","solutions":["Clamp the value to [min, max] for that param (see EFFECT_REGISTRY[type]['params'][name]).","Fetch param schemas via get_available_effects() and enforce the same min/max/step on the client.","Round to the registered 'step' increment as well."],"exampleFix":"// before — pitch_shift min is -12\n{\"type\": \"pitch_shift\", \"params\": {\"semitones\": -20}}\n\n// after\n{\"type\": \"pitch_shift\", \"params\": {\"semitones\": -12}}","handlingStrategy":"validation","validationCode":"from backend.utils.effects import EFFECT_REGISTRY\n\ndef clamp_param(effect_type, name, value):\n    p = EFFECT_REGISTRY[effect_type][\"params\"][name]\n    return max(p[\"min\"], min(p[\"max\"], value))","typeGuard":"from backend.utils.effects import EFFECT_REGISTRY\n\ndef in_range(effect_type, name, value) -> bool:\n    p = EFFECT_REGISTRY[effect_type][\"params\"][name]\n    return isinstance(value, (int, float)) and p[\"min\"] <= value <= p[\"max\"]","tryCatchPattern":"try:\n    create_preset(data, db)\nexcept ValueError as e:\n    if \"must be between\" in str(e):\n        # parse range, re-clamp, resubmit\n        ...\n    raise","preventionTips":["Bind UI inputs to the same min/max/step from EFFECT_REGISTRY.","Validate the whole chain client-side before submit."],"tags":["effects","validation","presets","ranges"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}