{"record":{"id":"8c8d4d9b9646421b","repo":"dgtlmoon/changedetection.io","slug":"invalid-value","errorCode":null,"errorMessage":"Invalid value.","messagePattern":"Invalid value\\.","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"warning","filePath":"changedetectionio/forms.py","lineNumber":818,"sourceCode":"        if not data:\n            return\n\n        # normalize into list of lines\n        if isinstance(data, str) and self.split_lines:\n            lines = data.splitlines()\n        elif isinstance(data, (list, tuple)):\n            lines = data\n        else:\n            lines = [data]\n\n        for line in lines:\n            stripped = line.strip()\n            if not stripped:\n                if self.allow_empty:\n                    continue\n                raise ValidationError(self.message or _l(\"Empty value not allowed.\"))\n            if not self.pattern.match(stripped):\n                raise ValidationError(self.message or _l(\"Invalid value.\"))\n\ndef visual_browser_choices():\n    \"\"\"Browsers that can render the Add-Watch live preview, as RadioField choices.\n\n    Lazy import (the add_watch_ui blueprint imports this module) and empty outside an\n    app context, because WTForms evaluates a choices callable on field construction.\n    \"\"\"\n    from flask import current_app, has_app_context\n    from changedetectionio.blueprint.add_watch_ui import browser_config\n\n    if not has_app_context():\n        return []\n    datastore = current_app.config.get('DATASTORE')\n    return browser_config.radio_choices(datastore) if datastore else []\n\n\nclass quickWatchForm(Form):\n    url = StringField('URL', validators=[validateURL()])","sourceCodeStart":800,"sourceCodeEnd":836,"githubUrl":"https://github.com/dgtlmoon/changedetection.io/blob/5d9c7c6da76340597243e8163c4f2439237fa0e8/changedetectionio/forms.py#L800-L836","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the pattern the field was constructed with and correct the offending line","Strip or normalize pasted text (remove smart quotes, BOMs, non-breaking spaces) before submitting","Test each line individually against the pattern to find the failing entry"],"exampleFix":"# before\npattern = re.compile(r'^\\w+$')\n# after\npattern = re.compile(r'^[\\w-]+$')  # allow dashes if values contain them","handlingStrategy":"validation","validationCode":"import re\npattern = re.compile(r'^\\w+$')  # use the same pattern as the validator\nbad = [l.strip() for l in value.splitlines() if l.strip() and not pattern.match(l.strip())]\nassert not bad, f'Lines failing pattern: {bad}'","typeGuard":"def lines_match(value: str, pattern: re.Pattern) -> bool:\n    return all(pattern.match(l.strip()) for l in value.splitlines() if l.strip())","tryCatchPattern":null,"preventionTips":["Test each line against the field's regex before submit","Normalize pasted text (strip smart quotes/BOMs/non-breaking spaces)"],"tags":["wtforms","validation","regex","form-input"],"backgroundTag":"form-validation-failed","analyzedSha":"5d9c7c6da76340597243e8163c4f2439237fa0e8","analyzedAt":"2026-08-27T19:41:16.067Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}