The instance of entity type 'Customer' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked
means one DbContext was handed two different objects for the same row. The usual
cause is loading an entity and then calling Update or Attach with a new
object carrying the same id. Change the loaded object instead, and the error goes away.
In short: find the entity, copy the incoming values onto it, call SaveChanges. No
Update call is needed, because the context already tracks it.
The error
System.InvalidOperationException: The instance of entity type 'Customer' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.ThrowIdentityConflict(InternalEntityEntry entry)
With EnableSensitiveDataLogging() switched on, the same failure names the key:
System.InvalidOperationException: The instance of entity type 'Customer' cannot be tracked because another instance with the key value '{Id: 1}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
Why it happens
A DbContext keeps an identity map: for each entity type and key it tracks exactly
one object, so that SaveChanges knows which object's values to write. This
endpoint breaks that rule within a single request:
var existing = await db.Customers.FirstOrDefaultAsync(c => c.Id == id);
if (existing is null) return Results.NotFound();
var updated = new EfLab.E3.Customer { Id = id, Name = input.Name, Email = input.Email };
db.Customers.Update(updated);
The existence check is a tracking query, so customer 1 is already in the map. Update
then tries to start tracking a second object with key 1, and the context throws rather than pick
one. In ASP.NET Core, AddDbContext already gives each request its own context, so
the two instances almost always meet inside one request, as here.
The fix
var customer = await db.Customers.FindAsync(id);
if (customer is null) return Results.NotFound();
customer.Name = input.Name;
customer.Email = input.Email;
await db.SaveChangesAsync();
The change tracker compares the object with the snapshot it took when loading it, and writes only what changed:
UPDATE "Customers" SET "Email" = @p0, "Name" = @p1
WHERE "Id" = @p2;
The second-best fix is to make the check a no-tracking read,
db.Customers.AsNoTracking().FirstOrDefaultAsync(...), and keep the
Update call. That stops the error, but Update marks every property as
modified. The lab's statement became
SET "Email" = @p0, "Name" = @p1, "Phone" = @p2, and because the new object had
no phone number, the customer's stored phone was overwritten with null. Only use
that route when the incoming object really carries every column.
How it was reproduced
A minimal API from dotnet new web with Microsoft.EntityFrameworkCore 10.0.12 and
Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3, a Customer table seeded with two
fictional customers, and a PUT endpoint that loaded the customer and then called
Update on a new object with the same id. It returned 500 with the error above; the
tracked-instance version returned 200 and kept the phone number. .NET SDK 10.0.401 on Windows,
PostgreSQL 18.4 in the postgres:18 Docker image.
Frequently asked
- How do I fix entity cannot be tracked because another instance with the same key is already being tracked?
- Update the instance the context already tracks instead of attaching a new one: load it with Find or FirstOrDefault, copy the new values onto it, and call SaveChanges. Alternatively load it with AsNoTracking, but then Update writes every column.
- How do I see which key value is already being tracked in EF Core?
- Turn on EnableSensitiveDataLogging on the DbContext options in development. The message then shows the conflicting key, for example {Id: 1}, instead of only the key property name. Leave it off in production because it also logs parameter values.
- Does AsNoTracking fix the cannot be tracked error?
- It removes the first tracked instance, so a later Update or Attach no longer collides. The cost is that Update marks every property as modified, so any column missing from the new object is overwritten, which set a stored phone number to null in this reproduction.
More decoded errors in the Fixes category. For when to track
and when to read with AsNoTracking, see
the SQL and EF Core interview questions.