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 firstmigrate - migrations were deleted, squashed, renamed, or regenerated after being applied somewhere
- rows were inserted or deleted in
django_migrationsby hand, or with--fakein 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.
Quick answer
- Back up the database first.
- Read the message: migration X is applied before dependency Y. Compare the
django_migrationstable with the migration files for both apps. - If Y's changes already exist in the schema (tables and columns are present), record Y as applied with
migrate app_label Y --fake. - If Y's changes do not exist, you cannot fake it; create the schema changes, or roll back X carefully, depending on data.
- For custom user model changes after the initial migration, follow a planned migration path rather than editing history ad hoc.
- In development databases with no valuable data, recreating the database is often the fastest correct fix.
- Verify with
showmigrations,migrate --plan, andmakemigrations --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__"),
]
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;
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.
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:
- 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. - Create its initial migration, and fake-apply it after confirming the table matches.
- Fix the history order so dependencies are satisfied, typically by inserting the new app's
0001_initialrow with anappliedtimestamp before the dependent migrations, in a transaction, with a backup. - Switch
AUTH_USER_MODELand 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:
- Restore the original migration files from version control if possible; that is the cleanest fix.
- If regenerated migrations must stay, verify that the current models match the database schema (
python manage.py makemigrations --check --dry-runagainst a database where history is aligned, or comparesqlmigrateoutput with the actual schema). - 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 --checkin CI. - Define a custom user model at project start, even if it is identical to the default.
- Avoid manual
--fakein production without a written procedure and backup. - Keep one migration history per app across all branches that will be merged.
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 --planshows no errors and only expected pending migrationsmakemigrations --checkreports 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_MODELon an existing database without a migration plan. - Removing replaced migrations right after creating a squash.
Next steps
- For failures during a release, see Django Migration Failed During Deploy: Recovery Playbook.
- Prevent release problems with Django Migration Strategy for Safe Deployments.
- Make backups reliable with Backup and Restore PostgreSQL for Django Apps.
- Plan heavy changes with Run Schema Changes on Large Tables in Django Safely.
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.