Fixing EF Core Pending Model Changes Errors

After upgrading to EF Core 9, the migration step that has worked for years fails immediately: System.InvalidOperationException: An error was generated for warning 'Microsoft.EntityFrameworkCore.Migrations.PendingModelChangesWarning': The model for context 'ShopContext' has pending changes. Add a new migration before updating the database. Nobody changed the model on purpose. Running dotnet ef migrations add Check produces a migration that updates seed rows with a new timestamp, or re-creates an index with a different filter string, or does nothing visible at all. EF Core 9 started treating any difference between the current model and the latest migration snapshot as an error when applying migrations. That is a useful guardrail β€” it catches model changes shipped without their migration β€” but it also exposes long-hidden non-determinism. This guide shows how to find what EF thinks changed, fix the genuine and the spurious cases, and turn the check into a CI gate. It belongs to Entity Framework Core Migrations.

What EF Compares EF Core builds the current model from entity classes and OnModelCreating configuration. It compares it with the model stored in the latest migration's snapshot. If they differ, Migrate and database update raise PendingModelChangesWarning as an error in EF Core 9. Differences come from genuine model edits, non-deterministic configuration such as DateTime.Now in HasData, or provider/version changes. What EF Compares Current model entities + OnModelCreating Model snapshot from latest migration Compare EF Core 9: error if different Genuine edit migration missing Non-deterministic config DateTime.Now, Guid.NewGuid() Provider / EF upgrade annotation differences
The check compares two models, not the database β€” a pending change can exist even when the database is perfectly up to date.

Symptom / Error Signatures

The EF Core 9 error, raised from Migrate(), MigrateAsync(), dotnet ef database update or a migration bundle:

System.InvalidOperationException: An error was generated for warning
'Microsoft.EntityFrameworkCore.Migrations.PendingModelChangesWarning':
The model for context 'ShopContext' has pending changes. Add a new migration before updating the database.
This exception can be suppressed or logged by passing event ID 'RelationalEventId.PendingModelChangesWarning'
to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'.

Related symptoms: dotnet ef migrations has-pending-model-changes (EF Core 8+) exits non-zero; every new migration you scaffold contains the same unexplained UpdateData calls; or scaffolding an empty migration produces operations touching seed data or index filters that nobody edited.

Root Cause Analysis

The snapshot file records the model as of the latest migration. When you apply migrations, EF Core 9 builds the current model from code and compares it with that snapshot; any difference means the code expects a schema that no migration creates. There are three families of cause:

Cause Example Correct fix
genuine model change without a migration new property, changed max length, new index add the migration
non-deterministic model configuration HasData(new { CreatedAt = DateTime.Now }), Guid.NewGuid() in seed data make values constant
environment-dependent configuration model built differently per environment or culture make OnModelCreating deterministic
EF or provider upgrade changing annotations new default annotations after upgrading add a (usually no-op) migration to refresh the snapshot

Non-deterministic seed data is the classic hidden case. HasData values become part of the model; if one is computed at runtime, every build produces a different model, the snapshot never matches, and each new migration contains UpdateData statements that rewrite seed rows. Before EF Core 9 this was merely noisy; now it blocks deploys.

Suppressing the warning is possible but defeats the point: the check exists to stop a release from running against a schema that lacks what its model expects β€” the precise situation that causes runtime failures such as Invalid column name after a deploy.

Triage for a Pending Model Change Decision tree. Scaffold a throwaway migration and inspect it. If it contains schema operations for something you changed, keep it as the real migration. If it contains UpdateData or InsertData for seed rows with changing values, make seed values constant. If it is empty or only annotation changes after an upgrade, commit it to refresh the snapshot. Triage for a Pending Model Change Throwaway migration has schema ops you made? yes no Keep it: this is the missing migration Only UpdateData on seed rows? yes no Make HasData values constant Upgrade noise: commit to refresh snapshot
Scaffolding a throwaway migration is the fastest way to see exactly what EF thinks changed.

Immediate Mitigation

1. See what EF thinks changed. Scaffold a throwaway migration and read it; remove it afterwards if it is not the fix.

# Shell Β· developer workstation Β· touches no database
dotnet ef migrations has-pending-model-changes --project src/Shop.Data --startup-project src/Shop.Api
dotnet ef migrations add PendingCheck --project src/Shop.Data --startup-project src/Shop.Api
cat src/Shop.Data/Migrations/*_PendingCheck.cs
dotnet ef migrations remove --project src/Shop.Data --startup-project src/Shop.Api   # if it is not the fix

2. If it is a genuine change, keep the migration. Review it like any other β€” additive, backward compatible, online where needed β€” and ship it. The deploy that failed was protecting you from running code that expects a column that does not exist.

3. If it is non-deterministic seed data, make the values constant.

// C# Β· Data/Configurations/OrderStatusConfiguration.cs Β· seed data must be deterministic
// WARNING: changing seed values later generates UpdateData migrations β€” that is expected and reviewable.
builder.HasData(
    new OrderStatus { Id = 1, Name = "Pending", CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc) },
    new OrderStatus { Id = 2, Name = "Paid",    CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc) });
// before: CreatedAt = DateTime.UtcNow  β†’ a different model on every build

Then scaffold one migration that brings the snapshot in line with the now-constant values, and confirm a second scaffold is empty.

4. Do not suppress the warning in production configuration. If an urgent deploy is blocked by upgrade-only annotation noise, the right fix is still to commit a refresh migration, which takes minutes. Suppression (ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning))) hides genuine missing migrations too.

Permanent Fix / Long-Term Pattern

Run the same check in CI that EF Core 9 runs at deploy time, so it fails in the pull request instead of in the pipeline. dotnet ef migrations has-pending-model-changes exits non-zero when the model and snapshot differ; add it to every build.

# YAML Β· CI job step Β· .NET SDK with dotnet-ef restored Β· no database connection needed
- name: Fail if the model has changes without a migration
  run: |
    dotnet tool restore
    dotnet ef migrations has-pending-model-changes \
      --project src/Shop.Data --startup-project src/Shop.Api

Keep OnModelCreating deterministic: no DateTime.Now, no random values, no configuration that depends on environment variables or the machine’s culture. Treat the model snapshot file as a reviewed artefact β€” changes to it should correspond to a migration in the same pull request β€” and resolve snapshot merge conflicts by regenerating rather than hand-merging, the same principle as resolving schema.rb merge conflicts. A missing migration caught this way is also the most common source of schema drift between environments, which the scheduled checks in detecting production schema drift against a desired state are designed to find.

Catching Model Drift Before Deploy Pipeline. Build the solution; a gate runs has-pending-model-changes and fails on differences; generate the idempotent script or bundle; a gate scans it for destructive statements; deploy runs migrations then the application. Catching Model Drift Before Deploy Build solution model pending changes? Script / bundle from same build scan destructive? Deploy migrate, then roll out add migration / fix seed review contract step fail
The pending-changes gate in CI means EF Core 9's deploy-time check should never fire in production.

Verification Checklist

Frequently Asked Questions

Why did this start failing after upgrading to EF Core 9? EF Core 9 changed PendingModelChangesWarning from a logged warning to an error by default when applying migrations. Differences between the model and the snapshot that were silently tolerated before now stop Migrate(), database update and bundles.

Is it safe to suppress the warning? It is possible but not advisable in production. The check exists to prevent deploying code whose model expects schema changes no migration makes. Fix the cause β€” add the migration or make the model deterministic β€” instead.

Why does HasData with DateTime.Now cause this? Seed data is part of the model. A value computed at build or run time differs every time the model is built, so the model never matches the snapshot and every scaffolded migration contains updates to the seed rows.

Does the check look at the database? No. It compares the current code model with the model snapshot from the latest migration. The database can be fully up to date and the check can still fail, because the missing piece is a migration that has not been written yet.