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 assumingINSERTreplaces. - 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
Updating an existing item? Use
UPDATE:UPDATE "orders" SET status = 'shipped' WHERE pk = 'ORDER#123' AND sk = 'META'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}));Treat it as success when the create is idempotent — if a retry hits
DuplicateItemExceptionfor an item your first attempt already wrote, catching and ignoring it is often the correct handling.Keep
INSERTwhen you rely on uniqueness — the exception is your create-only guard, the PartiQL equivalent ofattribute_not_exists()on aPutItemcondition.
Trying statements against real data is the quickest way to internalize the INSERT/UPDATE split — DynoTable's PartiQL editor runs statements against your live tables with inline diagnostics and quick-fixes, and the DynamoDB Expression Builder emits the equivalent native-API requests when you need PutItem semantics instead.
Reproduce it
A PartiQL INSERT for a primary key that already exists:
await client.send(
new PutItemCommand({
TableName: 'orders',
Item: {pk: {S: 'ORDER#1'}, sk: {S: 'META'}}
})
);
await client.send(
new ExecuteStatementCommand({
Statement: `INSERT INTO "orders" VALUE {'pk':'ORDER#1','sk':'META'}`
})
);Real output:
DuplicateItem: Duplicate primary key exists in table
HTTP 400Worth knowing if you test locally: DynamoDB Local surfaces this as DuplicateItem with the short message above, while the service API reference documents it as DuplicateItemException with a longer sentence. Match on the HTTP 400 plus the operation, not on the exact name or wording, or your handler will behave differently against Local than against the real table.
Related errors
- ConditionalCheckFailedException — the native-API twin:
attribute_not_exists()guard failing onPutItem. - ValidationException: Unexpected from source — the other common PartiQL rejection.
- Learn: PartiQL examples · PartiQL vs SQL
References
- ExecuteStatement — Amazon DynamoDB API Reference (DuplicateItemException)
- PartiQL insert statements for DynamoDB — Developer Guide
- PartiQL update statements for DynamoDB — Developer Guide
Last verified 2026-07-13 against the official AWS documentation linked above.
Reproduced 2026-07-26 against DynamoDB Local 2.x with AWS SDK for JavaScript v3.1095.0 — the output above is verbatim.