jamiepine/voicebox · error · ValueError

Effect '{effect_type}' at index {i}: param '{param_name}' mu

Error message

Effect '{effect_type}' at index {i}: param '{param_name}' must be between {pdef['min']} and {pdef['max']} (got {value})

What it means

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().

Source

Thrown at backend/services/effects.py:102

def update_preset(preset_id: str, data: EffectPresetUpdate, db: Session) -> Optional[EffectPresetResponse]:
    """Update a user effect preset. Cannot modify built-in presets."""
    preset = db.query(DBEffectPreset).filter_by(id=preset_id).first()
    if not preset:
        return None
    if preset.is_builtin:
        raise ValueError("Cannot modify built-in presets")

    if data.name is not None:
        preset.name = data.name
    if data.description is not None:
        preset.description = data.description
    if data.effects_chain is not None:

        chain_dicts = [e.model_dump() for e in data.effects_chain]
        error = validate_effects_chain(chain_dicts)
        if error:
            raise ValueError(error)
        preset.effects_chain = json.dumps(chain_dicts)

    db.commit()
    db.refresh(preset)
    return _preset_response(preset)


def delete_preset(preset_id: str, db: Session) -> bool:
    """Delete a user effect preset. Cannot delete built-in presets."""
    preset = db.query(DBEffectPreset).filter_by(id=preset_id).first()
    if not preset:
        return False
    if preset.is_builtin:
        raise ValueError("Cannot delete built-in presets")

    db.delete(preset)
    db.commit()
    return True

View on GitHub (pinned to 51f49dea19)

Solutions

  1. Clamp the value to [min, max] for that param (see EFFECT_REGISTRY[type]['params'][name]).
  2. Fetch param schemas via get_available_effects() and enforce the same min/max/step on the client.
  3. Round to the registered 'step' increment as well.

Example fix

// before — pitch_shift min is -12
{"type": "pitch_shift", "params": {"semitones": -20}}

// after
{"type": "pitch_shift", "params": {"semitones": -12}}
Defensive patterns

Strategy: validation

Validate before calling

from backend.utils.effects import EFFECT_REGISTRY

def clamp_param(effect_type, name, value):
    p = EFFECT_REGISTRY[effect_type]["params"][name]
    return max(p["min"], min(p["max"], value))

Type guard

from backend.utils.effects import EFFECT_REGISTRY

def in_range(effect_type, name, value) -> bool:
    p = EFFECT_REGISTRY[effect_type]["params"][name]
    return isinstance(value, (int, float)) and p["min"] <= value <= p["max"]

Try / catch

try:
    create_preset(data, db)
except ValueError as e:
    if "must be between" in str(e):
        # parse range, re-clamp, resubmit
        ...
    raise

Prevention

When it happens

Trigger: 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.

Common situations: Frontend slider bounds out of sync with the backend registry; manual JSON edit pushing an extreme value; stale cached effect schema on the client.

Related errors


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