django/django · error · CommandError
Migrations can be pruned only when an app is specified.
Error message
Migrations can be pruned only when an app is specified.
What it means
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.
Source
Thrown at django/core/management/commands/migrate.py:192
# graph, use the last replacement instead.
if (
target not in executor.loader.graph.nodes
and target in executor.loader.replacements
):
incomplete_migration = executor.loader.replacements[target]
target = incomplete_migration.replaces[-1]
targets = [target]
target_app_labels_only = False
elif options["app_label"]:
targets = [
key for key in executor.loader.graph.leaf_nodes() if key[0] == app_label
]
else:
targets = executor.loader.graph.leaf_nodes()
if options["prune"]:
if not options["app_label"]:
raise CommandError(
"Migrations can be pruned only when an app is specified."
)
if self.verbosity > 0:
self.stdout.write("Pruning migrations:", self.style.MIGRATE_HEADING)
to_prune = sorted(
migration
for migration in set(executor.loader.applied_migrations)
- set(executor.loader.disk_migrations)
if migration[0] == app_label
)
squashed_migrations_with_deleted_replaced_migrations = [
migration_key
for migration_key, migration_obj in executor.loader.replacements.items()
if any(replaced in to_prune for replaced in migration_obj.replaces)
]
if squashed_migrations_with_deleted_replaced_migrations:
self.stdout.write(
self.style.NOTICE(View on GitHub (pinned to ae25a40be0)
Solutions
- Specify the app: `python manage.py migrate myapp --prune`
- Run --prune for each app that has stale migration records individually
Example fix
// before python manage.py migrate --prune // after python manage.py migrate myapp --prune
Defensive patterns
Strategy: validation
Validate before calling
from django.core.management import call_command
app_label = "myapp"
if not app_label:
raise ValueError("--prune requires an app label; specify one")
call_command("migrate", app_label, prune=True) Type guard
def has_app_label_for_prune(app_label) -> bool:
"""True if a non-empty app label is provided for --prune."""
return isinstance(app_label, str) and bool(app_label.strip()) Try / catch
from django.core.management import call_command
from django.core.management.base import CommandError
try:
call_command("migrate", prune=True)
except CommandError as e:
if "pruned only when an app is specified" in str(e):
raise ValueError("Specify an app label: migrate myapp --prune")
raise Prevention
- Always pair --prune with an explicit app label
- Run --prune per-app to scope the cleanup safely
When it happens
Trigger: Running `python manage.py migrate --prune` without specifying an app label as the first positional argument.
Common situations: Developer reads about --prune in docs and runs it globally without an app, not realizing it requires a per-app scope.
Related errors
- Conflicting migrations detected; multiple leaf nodes in the
- Can't use run_syncdb with app '%s' as it has migrations.
- App '%s' does not have migrations.
- More than one migration matches '%s' in app '%s'. Please be
- Cannot find a migration matching '%s' from app '%s'.
AI-assisted analysis of django/django@ae25a40be0 (2026-08-06).
Data as JSON: /api/errors/0c28fbc250643feb.
Report an issue: GitHub.