dgtlmoon/changedetection.io · error · ValidationError
Watch protocol is not permitted or invalid URL format
Error message
Watch protocol is not permitted or invalid URL format
What it means
validate_url() in changedetectionio/forms.py calls is_safe_valid_url() from changedetectionio.validate_url and raises wtforms.ValidationError when the URL fails that check. The check enforces both URL syntax (via urlparse/validators) and a protocol allow-list, blocking schemes like file://, javascript://, and other non-http(s) protocols.
Source
Thrown at changedetectionio/forms.py:625
class validateURL(object):
"""
Flask wtform validators wont work with basic auth
"""
def __init__(self, message=None):
self.message = message
def __call__(self, form, field):
# This should raise a ValidationError() or not
validate_url(field.data)
def validate_url(test_url):
from changedetectionio.validate_url import is_safe_valid_url
if not is_safe_valid_url(test_url):
# This should be wtforms.validators.
raise ValidationError('Watch protocol is not permitted or invalid URL format')
class validateLLMApiBaseSafe(object):
"""Block private/loopback/reserved api_base values (SSRF) unless the operator
has opted in via ALLOW_IANA_RESTRICTED_ADDRESSES=true."""
def __call__(self, form, field):
from changedetectionio.validate_url import is_llm_api_base_safe
ok, reason = is_llm_api_base_safe(field.data)
if not ok:
raise ValidationError(reason)
class ValidateSinglePythonRegexString(object):
def __init__(self, message=None):
self.message = message
def __call__(self, form, field):View on GitHub (pinned to 5d9c7c6da7)
Solutions
- Prefix the URL with http:// or https:// and ensure it parses as an absolute URL
- Remove disallowed schemes (file:, ftp:, javascript:, data:) — only http/https are permitted
- Trim whitespace/quotes around the URL before submitting
- If integrating programmatically, pre-check with changedetectionio.validate_url.is_safe_valid_url before calling the API
Example fix
# before
validate_url('example.com/page')
# after
validate_url('https://example.com/page')
Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlparse
def url_ok(u: str) -> bool:
p = urlparse(u.strip())
return p.scheme in ('http', 'https') and bool(p.netloc)
# or use the library's own check:
from changedetectionio.validate_url import is_safe_valid_url
assert is_safe_valid_url('https://example.com')
Type guard
def is_http_url(u: str) -> bool:
p = urlparse(u.strip())
return p.scheme in ('http', 'https') and bool(p.netloc)
Try / catch
from wtforms import ValidationError
try:
validate_url(url)
except ValidationError as e:
flash(str(e))
Prevention
- Always submit absolute http(s) URLs
- Normalize with urljoin/urllib.parse before sending to the API
- Strip whitespace and quotes from pasted URLs
When it happens
Trigger: Calling validate_url(test_url) (directly or via a wtforms field validator) with a URL whose scheme is not http/https, is malformed, or fails is_safe_valid_url's parsing — e.g. 'ftp://example.com', 'file:///etc/passwd', 'javascript:alert(1)', or a string with no scheme at all.
Common situations: Users entering non-http schemes in the watch URL field, missing scheme ('example.com/page'), whitespace/control characters, or integrations passing un-normalized URLs. Also used as an SSRF guard so private/unsafe schemes are rejected.
Related errors
- A variable or function is not defined: {e}
- The following tokens used in the notification are not valid:
- XPath not permitted in this field!
- '%(expression)s' is not a valid XPath expression. (%(error)s
- {reason}
AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27).
Data as JSON: /api/errors/21e25549413cc0ba.
Report an issue: GitHub.