dgtlmoon/changedetection.io · warning · ValidationError

Invalid value.

Error message

Invalid value.

What it means

The same line-by-line validator raises ValidationError when a non-empty line fails to match the validator's compiled regex pattern. Each stripped line must fully match self.pattern (typically via re.match).

Source

Thrown at changedetectionio/forms.py:818

        if not data:
            return

        # normalize into list of lines
        if isinstance(data, str) and self.split_lines:
            lines = data.splitlines()
        elif isinstance(data, (list, tuple)):
            lines = data
        else:
            lines = [data]

        for line in lines:
            stripped = line.strip()
            if not stripped:
                if self.allow_empty:
                    continue
                raise ValidationError(self.message or _l("Empty value not allowed."))
            if not self.pattern.match(stripped):
                raise ValidationError(self.message or _l("Invalid value."))

def visual_browser_choices():
    """Browsers that can render the Add-Watch live preview, as RadioField choices.

    Lazy import (the add_watch_ui blueprint imports this module) and empty outside an
    app context, because WTForms evaluates a choices callable on field construction.
    """
    from flask import current_app, has_app_context
    from changedetectionio.blueprint.add_watch_ui import browser_config

    if not has_app_context():
        return []
    datastore = current_app.config.get('DATASTORE')
    return browser_config.radio_choices(datastore) if datastore else []


class quickWatchForm(Form):
    url = StringField('URL', validators=[validateURL()])

View on GitHub (pinned to 5d9c7c6da7)

Solutions

  1. Check the pattern the field was constructed with and correct the offending line
  2. Strip or normalize pasted text (remove smart quotes, BOMs, non-breaking spaces) before submitting
  3. Test each line individually against the pattern to find the failing entry

Example fix

# before
pattern = re.compile(r'^\w+$')
# after
pattern = re.compile(r'^[\w-]+$')  # allow dashes if values contain them
Defensive patterns

Strategy: validation

Validate before calling

import re
pattern = re.compile(r'^\w+$')  # use the same pattern as the validator
bad = [l.strip() for l in value.splitlines() if l.strip() and not pattern.match(l.strip())]
assert not bad, f'Lines failing pattern: {bad}'

Type guard

def lines_match(value: str, pattern: re.Pattern) -> bool:
    return all(pattern.match(l.strip()) for l in value.splitlines() if l.strip())

Prevention

When it happens

Trigger: Entering a line containing characters or a format not matching the pattern passed to the validator (e.g. whitespace inside a token, wrong scheme in a URL, non-numeric value) in a field using this validator class.

Common situations: Paste of values with invisible Unicode characters (non-breaking spaces, BOM); regex expecting ^https?:// and user entering bare domains; locale/keyboard producing different dash or quote characters.

Related errors


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