dgtlmoon/changedetection.io · error · ValueError
Invalid notification format: "{n_format}"
Error message
Invalid notification format: "{n_format}" What it means
The notification config dict validates its notification_format key on construction/kwargs update and raises ValueError if the value is not a key in valid_notification_formats (e.g. 'Text', 'Markdown', 'HTML'). It prevents downstream renderers from receiving an unknown format.
Source
Thrown at changedetectionio/notification_service.py:258
'watch_mime_type': None,
'watch_tag': None,
'watch_title': None,
'watch_url': 'https://WATCH-PLACE-HOLDER/',
'watch_uuid': 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', # Converted to 'watch_uuid' in create_notification_parameters
})
# Apply any initial data passed in
self.update({'watch_uuid': self.get('uuid')})
if initial_data:
self.update(initial_data)
# Apply any keyword arguments
if kwargs:
self.update(kwargs)
n_format = self.get('notification_format')
if n_format and not valid_notification_formats.get(n_format):
raise ValueError(f'Invalid notification format: "{n_format}"')
def set_random_for_validation(self):
import random, string
"""Randomly fills all dict keys with random strings (for validation/testing).
So we can test the output in the notification body
"""
for key in self.keys():
if key in ['uuid', 'time', 'watch_uuid', 'change_datetime'] or key.startswith('diff'):
continue
rand_str = 'RANDOM-PLACEHOLDER-'+''.join(random.choices(string.ascii_letters + string.digits, k=12))
self[key] = rand_str
def __setitem__(self, key, value):
if key == 'notification_format' and isinstance(value, str) and not value.startswith('RANDOM-PLACEHOLDER-'):
if not valid_notification_formats.get(value):
raise ValueError(f'Invalid notification format: "{value}"')
super().__setitem__(key, value)View on GitHub (pinned to 5d9c7c6da7)
Solutions
- Set notification_format to one of the keys of valid_notification_formats (check that dict for the accepted values, e.g. 'Text', 'Markdown', 'HTML')
- If migrating old data, map legacy format names to current ones before constructing
- Leave notification_format unset to use the default rather than guessing
Example fix
# before
notif = notification({'notification_format': 'text'})
# after
notif = notification({'notification_format': 'Text'}) Defensive patterns
Strategy: validation
Validate before calling
from changedetectionio.notification_service import valid_notification_formats
fmt = fmt if fmt in valid_notification_formats else 'Text'
notif = notification({'notification_format': fmt, ...}) Type guard
def is_valid_notification_format(v: str) -> bool:
from changedetectionio.notification_service import valid_notification_formats
return v in valid_notification_formats Try / catch
try:
notif = notification(cfg)
except ValueError as e:
cfg['notification_format'] = 'Text'
notif = notification(cfg) Prevention
- Only use format names that are keys of valid_notification_formats
- Validate externally sourced config before constructing notification objects
- Add a normalization step when importing watches from other instances
When it happens
Trigger: Constructing the notification object with notification_format='Bla' or loading a stored watch/app config whose notification_format string was edited by hand or came from an older version that supported a since-removed format.
Common situations: Editing datastore JSON directly and typo'ing the format; upgrading changedetection.io where a format name changed; API callers passing arbitrary strings instead of the documented enum.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Invalid notification format: "{value}"
- RegEx '%s' is not a valid regular expression.
- Empty value not allowed.
- Invalid value.
- Bounding box value is too long
AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27).
Data as JSON: /api/errors/e692fb8252296346.
Report an issue: GitHub.