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.