DuplicateItemException: chiave primaria duplicata

In breve — Un INSERT PartiQL è una creazione rigorosa: se un Item con la stessa chiave primaria esiste già, DynamoDB lancia DuplicateItemException invece di sovrascriverlo. Per modificare l'Item esistente usa UPDATE PartiQL; per ottenere la semantica replace-if-exists di PutItem, usa PutItem stesso — l'INSERT PartiQL deliberatamente non sostituisce mai.

Cosa significa

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

Il data-plane PartiQL (ExecuteStatement / ExecuteTransaction / BatchExecuteStatement) mappa INSERT a una creazione condizionale — riesce solo quando nessun Item con quella chiave primaria esiste. È il default opposto rispetto al PutItem nativo, che sostituisce silenziosamente un Item esistente. Se vieni da SQL aspettandoti che INSERT fallisca su una chiave duplicata, è esattamente questo comportamento; se ti aspettavi una semantica di upsert, questo errore è la sorpresa.

Perché succede

  • L'Item esiste genuinamente già — un retry, un replay, o due writer in race sulla stessa chiave che emettono entrambi INSERT.
  • Volevi un aggiornamento, non una creazione — porting di codice in stile PutItem a PartiQL presumendo che INSERT sostituisca.
  • Un loop di retry non idempotente — il primo tentativo è riuscito ma la risposta è andata persa (timeout), e il retry reinserisce la stessa chiave.
  • Una chiave sintetica non unica — la partition/sort key che componi collide più spesso di quanto pensi (es. un timestamp con precisione al secondo).

Come risolverlo

  1. Stai aggiornando un Item esistente? Usa UPDATE:

    UPDATE "orders" SET status = 'shipped' WHERE pk = 'ORDER#123' AND sk = 'META'
  2. Vuoi replace-if-exists (semantica PutItem)? Chiama PutItem — PartiQL non ha uno statement di upsert, e il default dell'API nativa è esattamente la sovrascrittura che cerchi:

    await client.send(new PutItemCommand({TableName: 'orders', Item: item}));
  3. Trattalo come un successo quando la creazione è idempotente — se un retry incontra DuplicateItemException per un Item che il tuo primo tentativo ha già scritto, intercettarlo e ignorarlo è spesso la gestione corretta.

  4. Mantieni INSERT quando fai affidamento sull'unicità — l'eccezione è il tuo guard create-only, l'equivalente PartiQL di attribute_not_exists() su una condizione PutItem.

Provare gli statement contro dati reali è il modo più rapido per interiorizzare la divisione INSERT/UPDATEl'editor PartiQL di DynoTable esegue gli statement sulle tue tabelle live con diagnostica inline e quick-fix, e il DynamoDB Expression Builder emette le richieste equivalenti dell'API nativa quando ti serve invece la semantica di PutItem.

Riproducilo

Un INSERT PartiQL per una chiave primaria che esiste già:

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'}`
  })
);

Output reale:

DuplicateItem: Duplicate primary key exists in table
HTTP 400

Vale la pena saperlo se testi in locale: DynamoDB Local espone questo errore come DuplicateItem con il messaggio breve qui sopra, mentre il riferimento dell'API del servizio lo documenta come DuplicateItemException con una frase più lunga. Fai il match sull'HTTP 400 più l'operazione, non sul nome o sulla formulazione esatti, altrimenti il tuo handler si comporterà diversamente contro Local rispetto alla tabella reale.

Errori correlati

Riferimenti

Ultima verifica 2026-07-13 rispetto alla documentazione ufficiale AWS collegata sopra.

Riprodotto il 2026-07-26 su DynamoDB Local 2.x con AWS SDK for JavaScript v3.1095.0 — l'output qui sopra è riportato alla lettera.

Lavora con DynamoDB senza la Console

Un client desktop veloce per DynamoDB che esegue il vero SQL che DynamoDB non può — JOINs, GROUP BY, aggregazioni — con modifica visuale e un agente AI sulle tue chiavi Bedrock.

Prova gratuita di 30 giorni, senza carta di credito — poi il piano Free senza limiti di tempo.