dgtlmoon/changedetection.io · warning · ValidationError
A variable or function is not defined: {e}
Error message
A variable or function is not defined: {e} What it means
Raised by a WTForms custom validator in changedetectionio when a notification body template references a Jinja2 variable or function that is undefined at render time. The validator renders the joined notification fields with jinja2_env.from_string(...).render() and catches jinja2.exceptions.UndefinedError, converting it into a wtforms.ValidationError so the form rejects the input. It means the template syntax is fine but a name used inside {{ ... }} or {% ... %}} does not exist in the rendering context.
Source
Thrown at changedetectionio/forms.py:595
# Might be a list of text, or might be just text (like from the apprise url list)
joined_data = ' '.join(map(str, field.data)) if isinstance(field.data, list) else f"{field.data}"
try:
# Use the shared helper to create a properly configured environment
jinja2_env = create_jinja_env(loader=BaseLoader)
# Add notification tokens for validation
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):View on GitHub (pinned to 5d9c7c6da7)
Solutions
- Check the notification token list (extra_notification_tokens) and correct the token name / typo in the template
- Remove or wrap the offending variable with a default filter, e.g. {{maybe_missing|default('')}}
- If you extended the code, register the missing function/variable in jinja2_env.globals before validation runs
Example fix
# before
{{current_diff}}
# after
{{current_diff|default('')}}
Defensive patterns
Strategy: validation
Validate before calling
import jinja2
from jinja2.meta import find_undeclared_variables
def template_tokens_ok(template: str, allowed_tokens: set[str]) -> list[str]:
env = jinja2.Environment(undefined=jinja2.StrictUndefined)
ast = env.parse(template)
missing = find_undeclared_variables(ast) - allowed_tokens
try:
env.from_string(template).render({t: '' for t in allowed_tokens})
except jinja2.UndefinedError:
return ['runtime-undefined']
return sorted(missing)
Try / catch
try:
validate_notification_template(text)
except wtforms.ValidationError as e:
# show 'A variable or function is not defined: ...' to the user
report(e.message)
Prevention
- Keep a whitelist of valid notification tokens and autocomplete them in the UI
- Lint templates client-side before submission
- Use |default('') for optional tokens
When it happens
Trigger: Submitting a watch/notification form whose title or body uses a token like {{current_diff}} or a filter/function that is not registered in jinja2_env.globals (only field.extra_notification_tokens are injected). Any Jinja2 UndefinedError raised while rendering joined_data triggers this ValidationError.
Common situations: Typos in notification tokens ({{curent_diff}} instead of {{current_diff}}), using tokens only available in a different notification system (e.g. AppRise tokens), or calling a helper function that was never registered in jinja2_env.globals.
Related errors
- The following tokens used in the notification are not valid:
- 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/375ee7a4d4337ab8.
Report an issue: GitHub.