{"record":{"id":"0c28fbc250643feb","repo":"django/django","slug":"migrations-can-be-pruned-only-when-an-app-is-speci","errorCode":null,"errorMessage":"Migrations can be pruned only when an app is specified.","messagePattern":"Migrations can be pruned only when an app is specified\\.","errorType":"console","errorClass":"CommandError","httpStatus":null,"severity":"error","filePath":"django/core/management/commands/migrate.py","lineNumber":192,"sourceCode":"                # graph, use the last replacement instead.\n                if (\n                    target not in executor.loader.graph.nodes\n                    and target in executor.loader.replacements\n                ):\n                    incomplete_migration = executor.loader.replacements[target]\n                    target = incomplete_migration.replaces[-1]\n                targets = [target]\n            target_app_labels_only = False\n        elif options[\"app_label\"]:\n            targets = [\n                key for key in executor.loader.graph.leaf_nodes() if key[0] == app_label\n            ]\n        else:\n            targets = executor.loader.graph.leaf_nodes()\n\n        if options[\"prune\"]:\n            if not options[\"app_label\"]:\n                raise CommandError(\n                    \"Migrations can be pruned only when an app is specified.\"\n                )\n            if self.verbosity > 0:\n                self.stdout.write(\"Pruning migrations:\", self.style.MIGRATE_HEADING)\n            to_prune = sorted(\n                migration\n                for migration in set(executor.loader.applied_migrations)\n                - set(executor.loader.disk_migrations)\n                if migration[0] == app_label\n            )\n            squashed_migrations_with_deleted_replaced_migrations = [\n                migration_key\n                for migration_key, migration_obj in executor.loader.replacements.items()\n                if any(replaced in to_prune for replaced in migration_obj.replaces)\n            ]\n            if squashed_migrations_with_deleted_replaced_migrations:\n                self.stdout.write(\n                    self.style.NOTICE(","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/django/django/blob/ae25a40be07e8a749edf526df37c93e59d4a22c9/django/core/management/commands/migrate.py#L174-L210","documentation":"The --prune flag deletes entries from the django_migrations database table for migration files that no longer exist on disk. Because pruning operates per-app (it scopes to `migration[0] == app_label` at line 201), an app label is mandatory. Without one, the command cannot determine scope and refuses at line 191-193.","triggerScenarios":"Running `python manage.py migrate --prune` without specifying an app label as the first positional argument.","commonSituations":"Developer reads about --prune in docs and runs it globally without an app, not realizing it requires a per-app scope.","solutions":["Specify the app: `python manage.py migrate myapp --prune`","Run --prune for each app that has stale migration records individually"],"exampleFix":"// before\npython manage.py migrate --prune\n// after\npython manage.py migrate myapp --prune","handlingStrategy":"validation","validationCode":"from django.core.management import call_command\n\napp_label = \"myapp\"\nif not app_label:\n    raise ValueError(\"--prune requires an app label; specify one\")\ncall_command(\"migrate\", app_label, prune=True)","typeGuard":"def has_app_label_for_prune(app_label) -> bool:\n    \"\"\"True if a non-empty app label is provided for --prune.\"\"\"\n    return isinstance(app_label, str) and bool(app_label.strip())","tryCatchPattern":"from django.core.management import call_command\nfrom django.core.management.base import CommandError\n\ntry:\n    call_command(\"migrate\", prune=True)\nexcept CommandError as e:\n    if \"pruned only when an app is specified\" in str(e):\n        raise ValueError(\"Specify an app label: migrate myapp --prune\")\n    raise","preventionTips":["Always pair --prune with an explicit app label","Run --prune per-app to scope the cleanup safely"],"tags":["django","migrations","prune","migrate","cli"],"analyzedSha":"ae25a40be07e8a749edf526df37c93e59d4a22c9","analyzedAt":"2026-08-06T21:46:51.801Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}