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

  1. Set notification_format to one of the keys of valid_notification_formats (check that dict for the accepted values, e.g. 'Text', 'Markdown', 'HTML')
  2. If migrating old data, map legacy format names to current ones before constructing
  3. 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

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


AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27). Data as JSON: /api/errors/e692fb8252296346. Report an issue: GitHub.