Fix Django NodeNotFoundError: Migration Dependencies Reference Nonexistent Parent Node
Fix the Django NodeNotFoundError saying migration dependencies reference a nonexistent parent node: restore deleted or uncommitted migration files, repair dependencies after squashing or renaming apps, and handle third-party app version mismatches.
Problem statement
Every management command fails before it does anything, including runserver, migrate, and check:
django.db.migrations.exceptions.NodeNotFoundError: Migration orders.0015_add_discount
dependencies reference nonexistent parent node ('products', '0009_price_currency')
Django builds a graph of all migration files on disk when it starts. Each migration lists the migrations it depends on. Here, orders/0015 says it needs products/0009, but no file with that name exists in the products app in this checkout. The graph cannot be built, so nothing runs.
- Search for the missing file:
git log --all --oneline -- products/migrations/0009_price_currency.py. - If it was never committed, commit it from the machine that created it.
- If it was deleted or renamed, restore it or update the dependency to the new name.
- If the parent belongs to a third-party app, install the package version that contains it.
- After squashing, keep the old migrations until every environment has applied the squash.
Step-by-step solution
1. Find out where the file went
ls products/migrations/ | grep 0009
git log --all --oneline -- 'products/migrations/0009*'
git status products/migrations/
Three situations cover almost every case:
- The file is untracked on a developer's machine and never reached git.
- The file was deleted, renamed, or squashed away in a later commit.
- The parent is in an installed package and your environment has a different version.
2. Commit the missing migration
If a teammate ran makemigrations products and committed only the orders migration, the products file exists only on their laptop. Ask them to commit it, or regenerate it carefully: running makemigrations products yourself may produce a file with a different name and contents, which breaks environments that already applied the original. Prefer the original file.
Add a CI check so this cannot reach a deploy again:
python manage.py makemigrations --check --dry-run
It fails when models and migration files disagree, which catches forgotten files early.
3. Repair a renamed or deleted parent
If someone renamed 0009_price_currency.py to 0009_currency.py, update every dependency that points at the old name:
grep -rn "0009_price_currency" */migrations/
Change the tuple to ("products", "0009_currency"). If the database already recorded the old name as applied, also update django_migrations so the history matches:
UPDATE django_migrations SET name = '0009_currency'
WHERE app = 'products' AND name = '0009_price_currency';
Do this in every environment, ideally from a data step in your deploy script.
4. Squashed migrations removed too early
After squashmigrations, the squash file has a replaces list. Django can only switch to the squash in an environment once all replaced migrations are applied there. If you deleted the replaced files and another app's migration still depends on one of them, you get this error. Restore the old files, point dependencies at the squash, and remove the originals only after every environment, including developer databases and staging, has migrated past them. See Squash Django Migrations in Production.
5. Third-party app versions
dependencies reference nonexistent parent node ('django_celery_beat', '0019_alter_periodictasks_options')
Your app migration was generated against a newer version of the package than the one installed here. Pin the same version everywhere:
pip show django-celery-beat
pip install "django-celery-beat==2.7.0"
Lock files (uv.lock, poetry.lock, or hashed requirements) prevent different environments from drifting.
Explanation
The migration loader imports every module in each app's migrations package and creates a node per migration and an edge per dependency. A dependency on a node that does not exist is a broken graph, and Django refuses to guess. That is why the error appears even for commands that never touch the database: the graph is built during startup checks. It also explains why the database state does not matter. The fix is always about which files are present.
Edge cases / notes
__init__.pymissing. If an app'smigrationsfolder lost its__init__.py, Django does not treat it as a package and every migration in it is "missing"..dockerignorepatterns. A pattern like**/0*.pymeant for something else can drop migration files from the image. Check the image contents.MIGRATION_MODULES. Overriding where an app's migrations live, common in tests, can point dependencies at modules that do not exist in production.
Next steps
- If files exist but the database disagrees, see Fix Django InconsistentMigrationHistory.
- Two branches both added migrations? Read Fix Django Conflicting Migrations.
- Recover a stalled release with Fix Django Migration Failed During Deploy.
FAQ
Can I fix this by faking migrations?
No. Faking writes rows to django_migrations, but this error happens before the database is consulted. The missing file must be restored or the dependency changed.
Why does it work on my laptop?
Your laptop has the file, probably untracked. The server or CI only has what git contains.
Can I just delete the dependency line?
Only if the migration truly does not need that parent. Dependencies usually exist because a foreign key or field refers to the other app's table.
Should I regenerate all migrations from scratch?
Not on a project that has production data. Resetting migrations requires faking initial migrations everywhere and is easy to get wrong.