{"record":{"id":"a9403e686e63010f","repo":"django/django","slug":"bad-message-level-string-s-possible-values-ar","errorCode":null,"errorMessage":"Bad message level string: `%s`. Possible values are: %s","messagePattern":"Bad message level string: `(.+?)`\\. Possible values are: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"django/contrib/admin/options.py","lineNumber":1435,"sourceCode":"        self, request, message, level=messages.INFO, extra_tags=\"\", fail_silently=False\n    ):\n        \"\"\"\n        Send a message to the user. The default implementation\n        posts a message using the django.contrib.messages backend.\n\n        Exposes almost the same API as messages.add_message(), but accepts the\n        positional arguments in a different order to maintain backwards\n        compatibility. For convenience, it accepts the `level` argument as\n        a string rather than the usual level number.\n        \"\"\"\n        if not isinstance(level, int):\n            # attempt to get the level if passed a string\n            try:\n                level = getattr(messages.constants, level.upper())\n            except AttributeError:\n                levels = messages.constants.DEFAULT_TAGS.values()\n                levels_repr = \", \".join(\"`%s`\" % level for level in levels)\n                raise ValueError(\n                    \"Bad message level string: `%s`. Possible values are: %s\"\n                    % (level, levels_repr)\n                )\n\n        messages.add_message(\n            request, level, message, extra_tags=extra_tags, fail_silently=fail_silently\n        )\n\n    def save_form(self, request, form, change):\n        \"\"\"\n        Given a ModelForm return an unsaved instance. ``change`` is True if\n        the object is being changed, and False if it's being added.\n        \"\"\"\n        return form.save(commit=False)\n\n    def save_model(self, request, obj, form, change):\n        \"\"\"\n        Given a model instance save it to the database.","sourceCodeStart":1417,"sourceCodeEnd":1453,"githubUrl":"https://github.com/django/django/blob/ae25a40be07e8a749edf526df37c93e59d4a22c9/django/contrib/admin/options.py#L1417-L1453","documentation":"A ValueError raised in ModelAdmin.message_user when the level argument is a string that does not correspond to a constant in django.contrib.messages.constants (e.g. not one of DEBUG/INFO/SUCCESS/WARNING/ERROR uppercased). message_user tolerates a small set of level strings by getattr-ing the constants module; anything else raises with a list of valid possibilities.","triggerScenarios":"Calling self.message_user(request, 'hi', level='notice') where 'notice' is not a built-in level. Passing a custom string level without first registering it via messages.add_level. Passing a typo like 'sucess'.","commonSituations":"Trying to use a custom message level name that was never registered. Copying code that assumed an extended level set. Sending a level value loaded from a config file as a raw string.","solutions":["Use one of the built-in level strings: 'debug', 'info', 'success', 'warning', or 'error' (case is uppercased internally).","For a custom level, register it first with messages.add_level(...) and pass the resulting integer (not the name string) to message_user.","If the level comes from external input, validate it against the set of DEFAULT_TAGS before calling message_user."],"exampleFix":"// before\nself.message_user(request, 'Saved', level='notice')\n// after\nfrom django.contrib import messages\nself.message_user(request, 'Saved', level=messages.SUCCESS)\n# or a custom registered level:\n# messages.add_level('NOTICE', 25); self.message_user(request, 'Saved', level=25)","handlingStrategy":"validation","validationCode":"from django.contrib.messages import constants as msg_constants\n\ndef normalize_level(level):\n    if isinstance(level, int):\n        return level\n    upper = level.upper() if isinstance(level, str) else level\n    if not hasattr(msg_constants, upper):\n        raise ValueError(\n            f'Bad message level string: `{level}`. '\n            f'Use one of: debug, info, success, warning, error.'\n        )\n    return getattr(msg_constants, upper)","typeGuard":"from django.contrib.messages import constants as msg_constants\n\ndef is_valid_level_string(level) -> bool:\n    return isinstance(level, str) and hasattr(msg_constants, level.upper())","tryCatchPattern":"try:\n    self.message_user(request, msg, level=raw_level)\nexcept ValueError as e:\n    if 'Bad message level string' in str(e):\n        self.message_user(request, msg, level='info')  # safe fallback\n    else:\n        raise","preventionTips":["Pass integer message levels (messages.SUCCESS etc.) instead of strings to message_user.","If a level comes from config/external input, validate it against messages.constants before use.","Register custom levels once at import via messages.add_level and use the returned integer."],"tags":["django-admin","messages","configuration","value-error"],"analyzedSha":"ae25a40be07e8a749edf526df37c93e59d4a22c9","analyzedAt":"2026-08-06T21:46:51.801Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}