{"record":{"id":"85ef0f5f4c21818e","repo":"dgtlmoon/changedetection.io","slug":"bounding-box-must-be-in-format-x-y-width-height","errorCode":null,"errorMessage":"Bounding box must be in format: x,y,width,height (integers only)","messagePattern":"Bounding box must be in format: x,y,width,height \\(integers only\\)","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"warning","filePath":"changedetectionio/processors/image_ssim_diff/forms.py","lineNumber":23,"sourceCode":"from wtforms import SelectField, StringField, validators, ValidationError, IntegerField\nfrom flask_babel import lazy_gettext as _l\nfrom changedetectionio.forms import processor_text_json_diff_form\nimport re\n\nfrom changedetectionio.processors.image_ssim_diff import SCREENSHOT_COMPARISON_THRESHOLD_OPTIONS\n\n\ndef validate_bounding_box(form, field):\n    \"\"\"Validate bounding box format: x,y,width,height with integers.\"\"\"\n    if not field.data:\n        return  # Optional field\n\n    if len(field.data) > 100:\n        raise ValidationError(_l('Bounding box value is too long'))\n\n    # Should be comma-separated integers\n    if not re.match(r'^\\d+,\\d+,\\d+,\\d+$', field.data):\n        raise ValidationError(_l('Bounding box must be in format: x,y,width,height (integers only)'))\n\n    # Validate values are reasonable (not negative, not ridiculously large)\n    parts = [int(p) for p in field.data.split(',')]\n    for part in parts:\n        if part < 0:\n            raise ValidationError(_l('Bounding box values must be non-negative'))\n        if part > 10000:  # Reasonable max screen dimension\n            raise ValidationError(_l('Bounding box values are too large'))\n\n\ndef validate_selection_mode(form, field):\n    \"\"\"Validate selection mode value.\"\"\"\n    if not field.data:\n        return  # Optional field\n\n    if field.data not in ['element', 'draw']:\n        raise ValidationError(_l('Selection mode must be either \"element\" or \"draw\"'))\n","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/dgtlmoon/changedetection.io/blob/5d9c7c6da76340597243e8163c4f2439237fa0e8/changedetectionio/processors/image_ssim_diff/forms.py#L5-L41","documentation":"The bounding box validator requires the exact shape ^\\d+,\\d+,\\d+,\\d+$ — four non-negative integers separated by single commas, nothing else. Anything else (floats, spaces, labels, fewer/more parts) fails this regex and raises ValidationError.","triggerScenarios":"Submitting '10, 20, 300, 400' (spaces), '10.5,20,300,400' (floats), '10,20,300' (three values), or '10,20,300,400px' — all fail re.match before value range checks.","commonSituations":"Coordinates copied from browser devtools or design tools that include decimals, spaces, units, or brackets; users assuming width/height can be omitted; localized keyboards producing a comma variant character.","solutions":["Format the value as exactly four comma-separated integers with no spaces: x,y,width,height","Round float coordinates to integers before saving","If your data has spaces, strip them programmatically before form submission"],"exampleFix":"# before\nfield.data = '10.0, 20, 300, 400'\n# after\nfield.data = ','.join(str(int(float(p))) for p in '10.0,20,300,400'.split(','))","handlingStrategy":"validation","validationCode":"import re\nif not re.fullmatch(r'\\d+,\\d+,\\d+,\\d+', bbox or ''):\n    bbox = ','.join(str(int(float(p))) for p in bbox.split(','))  # normalize then re-check","typeGuard":"import re\ndef is_wellformed_bbox(s: str) -> bool:\n    return bool(re.fullmatch(r'\\d+,\\d+,\\d+,\\d+', s or ''))","tryCatchPattern":null,"preventionTips":["Round and join coordinates programmatically instead of hand-typing","Strip spaces/units from tool output before saving"],"tags":["wtforms","validation","regex","bounding-box"],"backgroundTag":"form-validation-failed","analyzedSha":"5d9c7c6da76340597243e8163c4f2439237fa0e8","analyzedAt":"2026-08-27T19:41:16.067Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}