{"record":{"id":"e692fb8252296346","repo":"dgtlmoon/changedetection.io","slug":"invalid-notification-format-n-format","errorCode":null,"errorMessage":"Invalid notification format: \"{n_format}\"","messagePattern":"Invalid notification format: \"(.+?)\"","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"changedetectionio/notification_service.py","lineNumber":258,"sourceCode":"            'watch_mime_type': None,\n            'watch_tag': None,\n            'watch_title': None,\n            'watch_url': 'https://WATCH-PLACE-HOLDER/',\n            'watch_uuid': 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',  # Converted to 'watch_uuid' in create_notification_parameters\n        })\n\n        # Apply any initial data passed in\n        self.update({'watch_uuid': self.get('uuid')})\n        if initial_data:\n            self.update(initial_data)\n\n        # Apply any keyword arguments\n        if kwargs:\n            self.update(kwargs)\n\n        n_format = self.get('notification_format')\n        if n_format and not valid_notification_formats.get(n_format):\n            raise ValueError(f'Invalid notification format: \"{n_format}\"')\n\n    def set_random_for_validation(self):\n        import random, string\n        \"\"\"Randomly fills all dict keys with random strings (for validation/testing). \n        So we can test the output in the notification body\n        \"\"\"\n        for key in self.keys():\n            if key in ['uuid', 'time', 'watch_uuid', 'change_datetime'] or key.startswith('diff'):\n                continue\n            rand_str = 'RANDOM-PLACEHOLDER-'+''.join(random.choices(string.ascii_letters + string.digits, k=12))\n            self[key] = rand_str\n\n    def __setitem__(self, key, value):\n        if key == 'notification_format' and isinstance(value, str) and not value.startswith('RANDOM-PLACEHOLDER-'):\n            if not valid_notification_formats.get(value):\n                raise ValueError(f'Invalid notification format: \"{value}\"')\n\n        super().__setitem__(key, value)","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/dgtlmoon/changedetection.io/blob/5d9c7c6da76340597243e8163c4f2439237fa0e8/changedetectionio/notification_service.py#L240-L276","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nnotif = notification({'notification_format': 'text'})\n# after\nnotif = notification({'notification_format': 'Text'})","handlingStrategy":"validation","validationCode":"from changedetectionio.notification_service import valid_notification_formats\nfmt = fmt if fmt in valid_notification_formats else 'Text'\nnotif = notification({'notification_format': fmt, ...})","typeGuard":"def is_valid_notification_format(v: str) -> bool:\n    from changedetectionio.notification_service import valid_notification_formats\n    return v in valid_notification_formats","tryCatchPattern":"try:\n    notif = notification(cfg)\nexcept ValueError as e:\n    cfg['notification_format'] = 'Text'\n    notif = notification(cfg)","preventionTips":["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"],"tags":["notification","config","validation","valueerror"],"backgroundTag":"invalid-config-value","analyzedSha":"5d9c7c6da76340597243e8163c4f2439237fa0e8","analyzedAt":"2026-08-27T19:41:16.067Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}