dgtlmoon/changedetection.io · warning · ValidationError
The following tokens used in the notification are not valid:
Error message
The following tokens used in the notification are not valid: {undefined} What it means
After successfully rendering the notification template, the validator statically parses it with jinja2_env.parse() and runs jinja2.meta.find_undeclared_variables on the AST. Any variable name found in the template that is not a known notification token is reported back as a comma-joined list in a wtforms.ValidationError. This is a stricter check than runtime UndefinedError: it catches variables even if the undefined policy would tolerate them.
Source
Thrown at changedetectionio/forms.py:603
static_token_placeholders = NotificationContextData()
static_token_placeholders.set_random_for_validation()
jinja2_env.globals.update(static_token_placeholders)
if hasattr(field, 'extra_notification_tokens'):
jinja2_env.globals.update(field.extra_notification_tokens)
jinja2_env.from_string(joined_data).render()
except TemplateSyntaxError as e:
raise ValidationError(f"This is not a valid Jinja2 template: {e}") from e
except UndefinedError as e:
raise ValidationError(f"A variable or function is not defined: {e}") from e
except jinja2.exceptions.SecurityError as e:
raise ValidationError(f"This is not a valid Jinja2 template: {e}") from e
# Check for undeclared variables
ast = jinja2_env.parse(joined_data)
undefined = ", ".join(find_undeclared_variables(ast))
if undefined:
raise ValidationError(
f"The following tokens used in the notification are not valid: {undefined}"
)
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):View on GitHub (pinned to 5d9c7c6da7)
Solutions
- Compare each name in the error list against the documented notification tokens and fix typos
- Remove unused/unknown placeholders from the template
- If a custom token is genuinely needed, add it to extra_notification_tokens for the field before validation
Example fix
# before
{{watch_ur}}
# after
{{watch_url}}
Defensive patterns
Strategy: validation
Validate before calling
from jinja2 import Environment
from jinja2.meta import find_undeclared_variables
def undeclared(template: str, known: set[str]) -> set[str]:
ast = Environment().parse(template)
return find_undeclared_variables(ast) - known
Prevention
- Publish the exact token list next to the template editor
- Automate token autocompletion from extra_notification_tokens
- Reject templates early client-side using find_undeclared_variables
When it happens
Trigger: A template that renders without error but references names not declared in the environment, e.g. {{my_custom_var}} where only tokens from field.extra_notification_tokens exist. find_undeclared_variables(ast) returns a non-empty set, so the form is rejected with the offending names listed.
Common situations: Typos in token names, tokens valid only in other notification contexts (e.g. AppRise/JSON placeholders), or using variables the sandbox does not predeclare (like loop or var names not set).
Related errors
- A variable or function is not defined: {e}
- Watch protocol is not permitted or invalid URL format
- XPath not permitted in this field!
- '%(expression)s' is not a valid XPath expression. (%(error)s
- Invalid JSON object for field: {value}
AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27).
Data as JSON: /api/errors/1244b686bde094c7.
Report an issue: GitHub.