Fix InconsistentMigrationHistory in Django

Fix Django InconsistentMigrationHistory errors safely: read the dependency message, compare history with files, handle custom user models, avoid data loss.

Problem statement

python manage.py migrate (or makemigrations) stops with:

django.db.migrations.exceptions.InconsistentMigrationHistory: Migration admin.0001_initial is
applied before its dependency accounts.0001_initial on database 'default'.

Django records applied migrations in the django_migrations table. Before migrating, it checks that every applied migration's dependencies are also recorded as applied. This error means the table says a migration ran while one of the migrations it depends on did not, which should be impossible if migrations were only ever applied in order by Django.

It happens when history and code diverge:

  • a custom user model (AUTH_USER_MODEL) was introduced after the project's first migrate
  • migrations were deleted, squashed, renamed, or regenerated after being applied somewhere
  • rows were inserted or deleted in django_migrations by hand, or with --fake in the wrong order
  • a database from one branch or environment is used with code from another

Because this touches the record of what the schema looks like, careless fixes (deleting migration files, dropping rows) can leave the database schema out of sync with Django's understanding of it, causing failures later.

Migration state versus filesMigration filesin the repositorydjango_migrationsapplied recordsDependency checkbefore migrateDatabase schemathe ground truthRepair means making the record honest about what the schema really contains.

Quick answer

  1. Back up the database first.
  2. Read the message: migration X is applied before dependency Y. Compare the django_migrations table with the migration files for both apps.
  3. If Y's changes already exist in the schema (tables and columns are present), record Y as applied with migrate app_label Y --fake.
  4. If Y's changes do not exist, you cannot fake it; create the schema changes, or roll back X carefully, depending on data.
  5. For custom user model changes after the initial migration, follow a planned migration path rather than editing history ad hoc.
  6. In development databases with no valuable data, recreating the database is often the fastest correct fix.
  7. Verify with showmigrations, migrate --plan, and makemigrations --check.

Step-by-step solution

1. Take a backup

pg_dump --format=custom --file=/var/backups/myapp-before-migration-fix.dump "$DATABASE_URL"

Any repair involves editing migration state. A backup lets you return to a known point.

2. Understand the dependency

Open the migration named as applied (X) and check its dependencies:

# django/contrib/admin/migrations/0001_initial.py (excerpt)
class Migration(migrations.Migration):
    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
        ("contenttypes", "__first__"),
    ]
reading the dependency messageInconsistentMigrationHistory: Migrationadmin.0001_initialisapplied before its dependencyaccounts.0001_initialon database'default'.1231Recorded as applied in django_migrations.2Needed first, but not recorded as applied.3The database alias being checked.Classic cause: a custom user model added after the first migratedjango-deployment.com
The fix depends on whether the dependency’s tables already exist.

swappable_dependency(settings.AUTH_USER_MODEL) resolves to the first migration of the app containing your user model. That is why admin.0001_initial depends on accounts.0001_initial when AUTH_USER_MODEL = "accounts.User".

3. Compare recorded history with files

python manage.py showmigrations accounts admin auth
SELECT app, name, applied
FROM django_migrations
WHERE app IN ('accounts', 'admin', 'auth')
ORDER BY applied;
match your caseFixA: tables already exist✓record it: migrate --fake that oneB: custom user added late✕careful manual repair, or rebuildC: migrations regeneratedrealign names with the recorded historyD: mixed branchesrestore or rebuild the databaseTake a backup before any --fakedjango-deployment.com
--fake only edits the history table; it never changes the schema.
ls accounts/migrations/

Look for:

  • migrations recorded in the table but missing as files (deleted or renamed)
  • files not recorded (never applied, or history was reset)
  • migrations recorded with timestamps that show the order they were actually applied

4. Case A: the dependency's schema already exists

Sometimes the tables for Y exist, but its row in django_migrations is missing, for example after an environment was built with --fake or restored partially.

Check the schema:

\d accounts_user

If the schema created by Y matches the database exactly, record it without running it:

python manage.py migrate accounts 0001 --fake
python manage.py migrate

--fake only inserts a row into django_migrations. Use it only when you have verified the schema matches the migration; faking a migration whose changes are missing leaves the database without tables or columns Django believes exist.

5. Case B: a custom user model added later

This is the most common production variant. The project started with the default auth.User, ran migrate (applying admin and auth migrations), and later switched AUTH_USER_MODEL to accounts.User. Django's documentation warns that changing AUTH_USER_MODEL after tables exist is not supported by makemigrations and requires manual work.

the late custom user modelFirst migrateauth.User, admin appliedAUTH_USER_MODEL addedaccounts.Useradmin now depends onitaccounts.0001migratedependency not appliedPlan for it earlyset before first migrateDjango documents this: set a custom user model at project startdjango-deployment.com
Existing user rows have to be copied into the new table as part of the repair.

Options:

No real data yet (new project, staging): recreate the database and apply migrations from scratch with the custom user model in place.

dropdb myapp_staging && createdb myapp_staging
python manage.py migrate

Existing production data: plan a migration that preserves users. A common approach:

  1. Create the custom user model in a new app with db_table = "auth_user", mirroring the existing fields, so it maps onto the existing table.
  2. Create its initial migration, and fake-apply it after confirming the table matches.
  3. Fix the history order so dependencies are satisfied, typically by inserting the new app's 0001_initial row with an applied timestamp before the dependent migrations, in a transaction, with a backup.
  4. Switch AUTH_USER_MODEL and deploy.

Editing django_migrations directly looks like this:

BEGIN;
INSERT INTO django_migrations (app, name, applied)
VALUES ('accounts', '0001_initial', (SELECT min(applied) - interval '1 second' FROM django_migrations));
COMMIT;

The applied timestamp is informational; Django's consistency check looks at whether dependencies are recorded, not at timestamps. Test the whole procedure on a restored copy of production before running it for real. Many teams find it safer to keep the existing user model and add a profile model instead.

6. Case C: migrations deleted, squashed, or regenerated

If someone deleted migration files and ran makemigrations again, the new 0001_initial may have the same name as the old one but different content, or different names altogether. The database history refers to migrations that no longer exist in code.

Repair approach:

  1. Restore the original migration files from version control if possible; that is the cleanest fix.
  2. If regenerated migrations must stay, verify that the current models match the database schema (python manage.py makemigrations --check --dry-run against a database where history is aligned, or compare sqlmigrate output with the actual schema).
  3. Remove stale rows for the app and fake the new initial migrations:
DELETE FROM django_migrations WHERE app = 'orders';
python manage.py migrate orders --fake-initial

--fake-initial marks initial migrations as applied if all tables they create already exist, and runs them otherwise. It does not verify columns in detail, so compare schema carefully first.

For squashed migrations, keep the replaced migrations in the codebase until every environment has applied the squash; removing them early causes this error on environments that were mid-way.

7. Case D: databases from different branches

A database migrated on a feature branch (with migrations that were later changed or never merged) used with main produces inconsistent history. In development, reset the database or roll back the branch-specific migrations while that branch is still checked out:

git checkout feature-branch
python manage.py migrate orders 0012
git checkout main
python manage.py migrate

Rolling back only works while the migration files that are being reversed still exist in the checked-out code.

8. Prevent recurrence

  • Never edit or delete migrations that have been applied to shared environments; add new migrations instead.
  • Commit migrations together with model changes, and run makemigrations --check in CI.
  • Define a custom user model at project start, even if it is identical to the default.
  • Avoid manual --fake in production without a written procedure and backup.
  • Keep one migration history per app across all branches that will be merged.
keep history and files in stepNever delete applied migrationssquash insteadCustom user model from day oneeven if emptyOne database per branch locallyor reset when switchingCI migrates from zerocatches graph problemsMigration files are history; treat them as append-onlydjango-deployment.com
A migrate from zero in CI is cheap insurance.

9. Verify

python manage.py showmigrations
python manage.py migrate --plan
python manage.py makemigrations --check --dry-run
python manage.py check

Verification check:

  • migrate --plan shows no errors and only expected pending migrations
  • makemigrations --check reports no changes, meaning models match migrations
  • the application runs queries against the affected models without ProgrammingError
  • a restored backup copy reproduces the fix before you apply it to production

Explanation

Migrations form a dependency graph, and django_migrations is Django's record of which nodes of that graph have been applied to a database. InconsistentMigrationHistory is a guard against applying new migrations on top of a record that cannot be true. The fix is always to make the record honest again: either by recording migrations whose changes truly exist, by creating the missing schema, or by rebuilding a disposable database. Editing the record without checking the real schema moves the inconsistency somewhere harder to see.

Edge cases / notes

Multiple databases

With database routers, check the error's on database '...' part. Each database has its own django_migrations table.

Third-party app upgrades

A package upgrade that restructured its migrations can trigger this on existing installations. Read the package's upgrade notes; they often include specific fake-migration steps.

Raw SQL changes outside migrations

Manual schema changes in production make --fake decisions unreliable. Capture all schema changes in migrations.

Common mistakes

  • Deleting all migration files and regenerating them on a project with production data.
  • Faking migrations without verifying the schema they describe exists.
  • Switching AUTH_USER_MODEL on an existing database without a migration plan.
  • Removing replaced migrations right after creating a squash.

Next steps

FAQ

What causes InconsistentMigrationHistory in Django?

The django_migrations table records a migration as applied while one of its dependencies is not recorded. It usually follows a late custom user model, deleted or regenerated migrations, or manual changes to migration history.

Is it safe to use migrate --fake to fix it?

Only when the schema changes of the migration you fake already exist in the database. Faking a migration that did not run leaves Django believing tables or columns exist when they do not.

Can I switch to a custom user model after running migrations?

It is possible but requires a careful, manual migration plan. On projects without valuable data, recreating the database is simpler; on production, test the procedure on a restored copy first.

Should I delete migrations and run makemigrations again?

Not for apps whose migrations are applied to shared or production databases. Regenerated migrations no longer match the recorded history. Restore the original files or follow a verified fake-initial procedure.

Keep going