{"record":{"id":"375ee7a4d4337ab8","repo":"dgtlmoon/changedetection.io","slug":"a-variable-or-function-is-not-defined-e","errorCode":null,"errorMessage":"A variable or function is not defined: {e}","messagePattern":"A variable or function is not defined: (.+?)","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"warning","filePath":"changedetectionio/forms.py","lineNumber":595,"sourceCode":"        # Might be a list of text, or might be just text (like from the apprise url list)\n        joined_data = ' '.join(map(str, field.data)) if isinstance(field.data, list) else f\"{field.data}\"\n\n        try:\n            # Use the shared helper to create a properly configured environment\n            jinja2_env = create_jinja_env(loader=BaseLoader)\n\n            # Add notification tokens for validation\n            static_token_placeholders = NotificationContextData()\n            static_token_placeholders.set_random_for_validation()\n            jinja2_env.globals.update(static_token_placeholders)\n            if hasattr(field, 'extra_notification_tokens'):\n                jinja2_env.globals.update(field.extra_notification_tokens)\n\n            jinja2_env.from_string(joined_data).render()\n        except TemplateSyntaxError as e:\n            raise ValidationError(f\"This is not a valid Jinja2 template: {e}\") from e\n        except UndefinedError as e:\n            raise ValidationError(f\"A variable or function is not defined: {e}\") from e\n        except jinja2.exceptions.SecurityError as e:\n            raise ValidationError(f\"This is not a valid Jinja2 template: {e}\") from e\n\n        # Check for undeclared variables\n        ast = jinja2_env.parse(joined_data)\n        undefined = \", \".join(find_undeclared_variables(ast))\n        if undefined:\n            raise ValidationError(\n                f\"The following tokens used in the notification are not valid: {undefined}\"\n            )\n\nclass validateURL(object):\n\n    \"\"\"\n       Flask wtform validators wont work with basic auth\n    \"\"\"\n\n    def __init__(self, message=None):","sourceCodeStart":577,"sourceCodeEnd":613,"githubUrl":"https://github.com/dgtlmoon/changedetection.io/blob/5d9c7c6da76340597243e8163c4f2439237fa0e8/changedetectionio/forms.py#L577-L613","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\n{{current_diff}}\n# after\n{{current_diff|default('')}}\n","handlingStrategy":"validation","validationCode":"import jinja2\nfrom jinja2.meta import find_undeclared_variables\n\ndef template_tokens_ok(template: str, allowed_tokens: set[str]) -> list[str]:\n    env = jinja2.Environment(undefined=jinja2.StrictUndefined)\n    ast = env.parse(template)\n    missing = find_undeclared_variables(ast) - allowed_tokens\n    try:\n        env.from_string(template).render({t: '' for t in allowed_tokens})\n    except jinja2.UndefinedError:\n        return ['runtime-undefined']\n    return sorted(missing)\n","typeGuard":null,"tryCatchPattern":"try:\n    validate_notification_template(text)\nexcept wtforms.ValidationError as e:\n    # show 'A variable or function is not defined: ...' to the user\n    report(e.message)\n","preventionTips":["Keep a whitelist of valid notification tokens and autocomplete them in the UI","Lint templates client-side before submission","Use |default('') for optional tokens"],"tags":["jinja2","wtforms","template-validation","changedetectionio","notifications"],"backgroundTag":"jinja2-undefined-variable","analyzedSha":"5d9c7c6da76340597243e8163c4f2439237fa0e8","analyzedAt":"2026-08-27T19:41:16.067Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}