Skip to content

Fix: Django Migration Conflict (Conflicting Migrations Cannot Be Applied)

FixDevs ·

Quick Answer

How to fix Django migration conflicts — why multiple leaf migrations conflict, how to merge conflicting migrations, resolve dependency chains, and set up a team workflow to prevent migration conflicts.

The Error

Running python manage.py migrate or python manage.py makemigrations fails with:

CommandError: Conflicting migrations detected; multiple leaf nodes in the
migration graph: (0003_add_email, 0003_add_phone) in app 'users'.
To fix them run 'python manage.py makemigrations --merge'.

Or:

django.db.migrations.exceptions.MigrationSchemaMissing: Unable to create the
django_migrations table (table "django_migrations" already exists)

Or when running showmigrations:

users
 [X] 0001_initial
 [X] 0002_add_username
 [ ] 0003_add_email
 [ ] 0003_add_phone      ← Two migrations with the same number

Why This Happens

Django migration conflicts occur when two migrations claim to be the “next” migration in the same app’s history — both have the same parent but were created independently:

  • Two developers created migrations in parallel — Developer A creates 0003_add_email on their branch; Developer B creates 0003_add_phone on their branch. When merged, Django sees two migrations with parent 0002, creating a fork in the graph.
  • Stale migration state — a migration was created based on an outdated model state.
  • Squashing went wrong — a squashed migration has incorrect dependencies.
  • Migrations not committed before branching — a developer forgets to commit migrations before branching, then makes a migration on the branch that conflicts with the one on the main branch.

Fix 1: Merge Conflicts with makemigrations —merge

The standard fix when Django itself detects the conflict:

# Django tells you exactly what to do
python manage.py makemigrations --merge

# If you have multiple apps with conflicts, specify the app
python manage.py makemigrations --merge users

# Review the generated merge migration before applying
cat users/migrations/0004_merge_0003_add_email_0003_add_phone.py

The generated merge migration looks like this — it simply depends on both conflicting migrations:

# users/migrations/0004_merge_0003_add_email_0003_add_phone.py
from django.db import migrations

class Migration(migrations.Migration):

    dependencies = [
        ('users', '0003_add_email'),
        ('users', '0003_add_phone'),
    ]

    operations = [
        # Empty — the merge migration just links the two branches
    ]

After creating the merge migration:

python manage.py migrate users
python manage.py showmigrations users
# All migrations should now show [X]

Fix 2: Resolve Conflicting Migrations Manually

When the auto-merge would produce incorrect ordering (e.g., one migration adds a column that the other migration also references), resolve manually:

Step 1 — identify which migrations conflict:

python manage.py showmigrations users
# users
#  [X] 0001_initial
#  [X] 0002_add_username
#  [ ] 0003_add_email      ← Conflicts with:
#  [ ] 0003_add_phone      ← This one

python manage.py migrate --check  # Returns non-zero if migrations are unapplied

Step 2 — decide on the correct order:

Determine which migration should come first logically. If add_phone depends on the email field being present, then add_email must run first.

Step 3 — rename and re-number one migration:

# Rename 0003_add_phone to 0004_add_phone to make it come after 0003_add_email
mv users/migrations/0003_add_phone.py users/migrations/0004_add_phone.py

Step 4 — update the dependencies in the renamed migration:

# users/migrations/0004_add_phone.py
class Migration(migrations.Migration):

    dependencies = [
        ('users', '0003_add_email'),  # Now depends on 0003_add_email, not 0002
    ]

    operations = [
        migrations.AddField(
            model_name='user',
            name='phone',
            field=models.CharField(max_length=20, blank=True),
        ),
    ]

Step 5 — apply:

python manage.py migrate users

Fix 3: Fix Conflicts in Development After git merge

The most common real-world scenario — you merge a branch and suddenly have conflicting migrations:

git merge feature/add-email-field
# Now you have:
# users/migrations/0003_add_email.py   (from main)
# users/migrations/0003_add_phone.py   (from your branch)

Option A — use makemigrations —merge (safe, recommended):

python manage.py makemigrations --merge --no-input
git add users/migrations/0004_merge_*.py
git commit -m "Merge migration conflict in users app"

Option B — delete your local migration and recreate it:

If you haven’t pushed your branch yet and your migration is simpler:

# 1. Unapply your local migration
python manage.py migrate users 0002  # Roll back to before your migration

# 2. Delete your conflicting migration
rm users/migrations/0003_add_phone.py

# 3. Recreate it — it will now depend on 0003_add_email
python manage.py makemigrations users
# Creates: 0004_add_phone.py with correct dependency

# 4. Apply
python manage.py migrate users

Option C — squash and restart (for local dev databases only):

# Reset all migrations for the app (DESTROYS DATA — dev only)
python manage.py migrate users zero   # Unapply all migrations
find users/migrations -name "0*.py" -not -name "0001_initial.py" -delete
python manage.py makemigrations users
python manage.py migrate users

Fix 4: Fix “django_migrations table already exists” Error

This happens when you try to initialize Django migrations on a database that already has the tables but not the migrations tracking table — common when migrating from a legacy schema:

# Create the migrations table without running any migrations
python manage.py migrate --run-syncdb

# Or — fake the initial migration to tell Django the schema already exists
python manage.py migrate users 0001 --fake-initial

--fake-initial tells Django to mark the initial migration as applied without actually running it — useful when the tables already exist from a syncdb or manual SQL.

For all apps at once:

python manage.py migrate --fake-initial

Fix 5: Prevent Migration Conflicts in Teams

Use a sequential naming convention:

# Bad — Django auto-names by content: 0003_add_email vs 0003_add_phone
python manage.py makemigrations

# Good — use --name to make intent clear (but numbering still conflicts)
python manage.py makemigrations users --name add_email_field

Establish a team workflow:

  1. Always pull and migrate before creating a new migration:

    git pull origin main
    python manage.py migrate
    python manage.py makemigrations
  2. Commit migrations immediately after creating them — never leave them uncommitted.

  3. Use a migration lock in CI — fail the build if unapplied migrations exist:

    # In CI before running tests
    python manage.py migrate --check
    # Returns exit code 1 if there are unapplied migrations — catches conflicts early
  4. Squash migrations periodically to clean up the history and reduce conflict surface:

    python manage.py squashmigrations users 0001 0010
    # Creates users/migrations/0001_squashed_0010_...py

Fix 6: Fix Dependency Errors in Migration Files

When a migration references a model or field from another app and the dependency is missing:

django.db.migrations.exceptions.NodeNotFoundError:
Migration users.0003_add_profile references model 'profiles.Profile'
from app 'profiles', which is not installed

Add the missing dependency explicitly:

# users/migrations/0003_add_profile.py
class Migration(migrations.Migration):

    dependencies = [
        ('users', '0002_add_username'),
        ('profiles', '0001_initial'),    # ← Add this dependency
    ]

    operations = [
        migrations.AddField(
            model_name='user',
            name='profile',
            field=models.OneToOneField(
                'profiles.Profile',
                on_delete=models.CASCADE,
                null=True,
            ),
        ),
    ]

Check the dependency graph:

# Show the full migration dependency graph
python manage.py showmigrations --plan

Fix 7: Fix Migrations on Production After a Bad Deploy

If a bad migration was deployed to production and needs to be rolled back:

Roll back to the previous migration:

# Check current state
python manage.py showmigrations users

# Roll back to the migration before the bad one
python manage.py migrate users 0005  # Reverts 0006 and later

# Verify
python manage.py showmigrations users

If the migration is not reversible (e.g., it drops a column), write a reverse migration:

class Migration(migrations.Migration):

    dependencies = [('users', '0005_safe_migration')]

    operations = [
        # Forward: removes the column
        migrations.RemoveField(model_name='user', name='legacy_field'),
        # This migration CANNOT be reversed automatically
        # Add database_backwards to make it reversible:
    ]

    def database_backwards(self, app_label, schema_editor, from_state, to_state):
        # Manually re-add the column for rollback
        schema_editor.execute(
            "ALTER TABLE users_user ADD COLUMN legacy_field VARCHAR(255) DEFAULT ''"
        )

Still Not Working?

Check for circular dependencies. If two apps depend on each other’s migrations, you get a circular dependency error. Resolve by moving the shared model to a third app or using a lazy reference:

# Instead of ForeignKey to a model in another app that depends on this one:
field=models.ForeignKey('other_app.Model', ...)  # Lazy string reference — avoids circular import

Check that INSTALLED_APPS includes all apps with migrations. If an app is missing from INSTALLED_APPS, Django cannot find its migrations and reports dependency errors.

Use python manage.py migrate --plan to see what migrations will be applied before running them:

python manage.py migrate --plan
# Lists every migration in the order it will be applied

For related Django and database issues, see Fix: Django OperationalError No Such Table and Fix: Prisma Migration Failed.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles