DuplicateItemException: duplicate primary key

TL;DR — A PartiQL INSERT is a strict create: if an item with the same primary key already exists, DynamoDB throws DuplicateItemException instead of overwriting it. To modify the existing item use PartiQL UPDATE; to get PutItem's replace-if-exists semantics, use PutItem itself — PartiQL INSERT deliberately never replaces.

What it means

DuplicateItemException: There was an attempt to insert an item with the
same primary key as an item that already exists in the DynamoDB table.

The PartiQL data-plane (ExecuteStatement / ExecuteTransaction / BatchExecuteStatement) maps INSERT to a conditional create — it succeeds only when no item with that primary key exists. That's the opposite default from the native PutItem, which silently replaces an existing item. If you came from SQL expecting INSERT to fail on a duplicate key, this is exactly that behavior; if you expected upsert semantics, this error is the surprise.

Why it happens

  • The item genuinely already exists — a retry, a replay, or two writers racing on the same key both issuing INSERT.
  • You wanted an update, not a create — porting PutItem-style code to PartiQL and assuming INSERT replaces.
  • A non-idempotent retry loop — the first attempt succeeded but the response was lost (timeout), and the retry re-inserts the same key.
  • A synthetic key that isn't unique — the partition/sort key you compose collides more often than you think (e.g. a timestamp at second precision).

How to fix it

  1. Updating an existing item? Use UPDATE:

    UPDATE "orders" SET status = 'shipped' WHERE pk = 'ORDER#123' AND sk = 'META'
  2. Want replace-if-exists (PutItem semantics)? Call PutItem — PartiQL has no upsert statement, and the native API's default is exactly the overwrite you're after:

    await client.send(new PutItemCommand({TableName: 'orders', Item: item}));
  3. Treat it as success when the create is idempotent — if a retry hits DuplicateItemException for an item your first attempt already wrote, catching and ignoring it is often the correct handling.

  4. Keep INSERT when you rely on uniqueness — the exception is your create-only guard, the PartiQL equivalent of attribute_not_exists() on a PutItem condition.

Trying statements against real data is the quickest way to internalize the INSERT/UPDATE split — the DynoTable desktop app runs PartiQL in a SQL workbench against your live tables, and the DynamoDB Expression Builder emits the equivalent native-API requests when you need PutItem semantics instead.

References

Last verified 2026-07-13 against the official AWS documentation linked above.

Work with DynamoDB without the Console

DynoTable is a fast desktop client for DynamoDB — browse tables, run SQL-style queries, and edit items locally.