dgtlmoon/changedetection.io · warning · ValidationError

Selection mode must be either "element" or "draw"

Error message

Selection mode must be either "element" or "draw"

What it means

This is a WTForms ValidationError raised by the custom validator validate_selection_mode in the image_ssim_diff processor settings form. It fires when the submitted selection_mode field is non-empty but is not one of the two allowed literal values ('element' or 'draw'). It exists to stop invalid crop/selection modes from reaching the screenshot-diff processor.

Source

Thrown at changedetectionio/processors/image_ssim_diff/forms.py:40

    if not re.match(r'^\d+,\d+,\d+,\d+$', field.data):
        raise ValidationError(_l('Bounding box must be in format: x,y,width,height (integers only)'))

    # Validate values are reasonable (not negative, not ridiculously large)
    parts = [int(p) for p in field.data.split(',')]
    for part in parts:
        if part < 0:
            raise ValidationError(_l('Bounding box values must be non-negative'))
        if part > 10000:  # Reasonable max screen dimension
            raise ValidationError(_l('Bounding box values are too large'))


def validate_selection_mode(form, field):
    """Validate selection mode value."""
    if not field.data:
        return  # Optional field

    if field.data not in ['element', 'draw']:
        raise ValidationError(_l('Selection mode must be either "element" or "draw"'))


class processor_settings_form(processor_text_json_diff_form):
    """Form for fast image comparison processor settings."""

    processor_config_min_change_percentage = IntegerField(
        _l('Minimum Change Percentage'),
        validators=[
            validators.Optional(),
            validators.NumberRange(min=1, max=100, message=_l('Must be between 0 and 100'))
        ],
        render_kw={"placeholder": "Use global default (0.1)"}
    )

    processor_config_pixel_difference_threshold_sensitivity = SelectField(
        _l('Pixel Difference Sensitivity'),
        choices=[
                    ('', _l('Use global default'))

View on GitHub (pinned to 5d9c7c6da7)

Solutions

  1. Set selection_mode to exactly 'element' or 'draw' (case-sensitive, lowercase)
  2. Leave the field empty/omitted if you don't need a selection mode
  3. If you added a new mode in a fork, extend the allowed list in validate_selection_mode in changedetectionio/processors/image_ssim_diff/forms.py:40

Example fix

// before
form_data = {'selection_mode': 'crop'}
// after
form_data = {'selection_mode': 'draw'}
Defensive patterns

Strategy: validation

Validate before calling

ALLOWED = {'element', 'draw', ''}
if form_data.get('selection_mode', '') not in ALLOWED:
    raise ValueError("selection_mode must be 'element' or 'draw'")

Type guard

def is_valid_selection_mode(v: str) -> bool:
    return v in ('element', 'draw')

Prevention

When it happens

Trigger: POSTing the processor settings form with selection_mode set to anything other than 'element' or 'draw' (e.g. 'freehand', 'crop', 'rect'). Only triggers when field.data is truthy — an empty value passes silently because the field is optional.

Common situations: Custom UI code or API clients writing the form directly; older frontends submitting a legacy mode name after an upgrade; hand-crafted curl/pytest form posts that guess the allowed values.

Related errors


AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27). Data as JSON: /api/errors/ce0a21556f8a29ea. Report an issue: GitHub.