{"record":{"id":"6d609d775204ff4a","repo":"django/django","slug":"the-app-label-s-is-not-a-valid-python-identifie","errorCode":null,"errorMessage":"The app label '%s' is not a valid Python identifier.","messagePattern":"The app label '(.+?)' is not a valid Python identifier\\.","errorType":"exception","errorClass":"ImproperlyConfigured","httpStatus":null,"severity":"error","filePath":"django/apps/config.py","lineNumber":36,"sourceCode":"        self.name = app_name\n\n        # Root module for the application e.g. <module 'django.contrib.admin'\n        # from 'django/contrib/admin/__init__.py'>.\n        self.module = app_module\n\n        # Reference to the Apps registry that holds this AppConfig. Set by the\n        # registry when it registers the AppConfig instance.\n        self.apps = None\n\n        # The following attributes could be defined at the class level in a\n        # subclass, hence the test-and-set pattern.\n\n        # Last component of the Python path to the application e.g. 'admin'.\n        # This value must be unique across a Django project.\n        if not hasattr(self, \"label\"):\n            self.label = app_name.rpartition(\".\")[2]\n        if not self.label.isidentifier():\n            raise ImproperlyConfigured(\n                \"The app label '%s' is not a valid Python identifier.\" % self.label\n            )\n\n        # Human-readable name for the application e.g. \"Admin\".\n        if not hasattr(self, \"verbose_name\"):\n            self.verbose_name = self.label.title()\n\n        # Filesystem path to the application directory e.g.\n        # '/path/to/django/contrib/admin'.\n        if not hasattr(self, \"path\"):\n            self.path = self._path_from_module(app_module)\n\n        # Module containing models e.g. <module 'django.contrib.admin.models'\n        # from 'django/contrib/admin/models.py'>. Set by import_models().\n        # None if the application doesn't have a models module.\n        self.models_module = None\n\n        # Mapping of lowercase model names to model classes. Initially set to","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/django/django/blob/ae25a40be07e8a749edf526df37c93e59d4a22c9/django/apps/config.py#L18-L54","documentation":"Raised in AppConfig.__init__ when the resolved app label fails Python's str.isidentifier() check. Django uses the label as a Python module-level identifier (e.g. for app_label.ModelName lookups, migrations, model _meta.app_label), so it must be a valid identifier. It is raised as django.core.exceptions.ImproperlyConfigured during app population.","triggerScenarios":"An INSTALLED_APPS entry resolves to a module whose last path component is not a valid identifier (e.g. 'my-app' or '2things'), or a custom AppConfig subclass sets label = 'some-thing' with a hyphen/digit-leading/space. The check at config.py:35 (`if not self.label.isidentifier()`) fires before any other init completes.","commonSituations":"Naming a Django app package with a hyphen (my-blog) instead of underscore (my_blog); copying a third-party package whose distribution name has a dash but importing the module by the wrong name; setting label on an AppConfig to a display string like 'My App'.","solutions":["Rename the app's Python package directory to use only [A-Za-z_][A-Za-z0-9_]* (e.g. my_blog, not my-blog).","If you cannot rename the package, create an apps.py with an AppConfig subclass declaring both `label = 'my_blog'` (a valid identifier) and `name = 'my-app'`, then reference that AppConfig in INSTALLED_APPS.","Ensure custom AppConfig.label values are valid identifiers — letters, digits (not leading), and underscores only."],"exampleFix":"# before\n# INSTALLED_APPS = ['my-blog']  -> last component 'my-blog' fails isidentifier()\n\n# after (option A): rename package\ndjango_blog/   # package dir renamed\n\n# after (option B): AppConfig override\n# my_blog/apps.py\nclass MyAppConfig(AppConfig):\n    name = 'my_blog'\n    label = 'my_blog'\n# settings.py: INSTALLED_APPS = ['my_blog.apps.MyAppConfig']","handlingStrategy":"validation","validationCode":"import keyword\nlabel = getattr(app_config_cls, 'label', app_name.rpartition('.')[2])\nif not label.isidentifier() or keyword.iskeyword(label):\n    raise ValueError(f'App label {label!r} is not a valid identifier; rename the package or set AppConfig.label.')","typeGuard":"import keyword\n\ndef is_valid_app_label(label: str) -> bool:\n    return isinstance(label, str) and label.isidentifier() and not keyword.iskeyword(label)","tryCatchPattern":"try:\n    app_config = MyAppConfig(name='myapp', module=app_module)\nexcept ImproperlyConfigured as e:\n    if 'not a valid Python identifier' in str(e):\n        # rename package or override label, then retry population\n        raise SystemExit(f'Fix app label: {e}')\n    raise","preventionTips":["Use only snake_case identifiers for app package directories.","Never set AppConfig.label to a display string.","Add a CI lint check that asserts every INSTALLED_APPS entry's last component passes str.isidentifier()."],"tags":["django","apps","config","identifier","installed-apps"],"analyzedSha":"ae25a40be07e8a749edf526df37c93e59d4a22c9","analyzedAt":"2026-08-06T21:46:51.801Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}