DynamoDB ConditionalCheckFailedException

TL;DR — Tu escritura llevaba un ConditionExpression que evaluó a false contra el item actual, así que DynamoDB rechazó la escritura y dejó el item intacto. Esto normalmente es lo esperado (concurrencia optimista, "crear si no existe") — captúralo y ramifica, no reintentes a ciegas.

Qué significa

A diferencia de una ValidationException, la petición estaba bien formada. DynamoDB evaluó tu condición y no se cumplió, así que el PutItem / UpdateItem / DeleteItem (o un item individual dentro de un TransactWriteItems) fue rechazado. Ningún dato cambió. Devuelve HTTP 400 y no es reintentable tal cual.

Por qué ocurre

  • Guarda attribute_not_exists(pk) en una creación — el item ya existe (un insert duplicado).
  • Guarda attribute_exists(pk) en una actualización/eliminación — el item ya no está.
  • Concurrencia optimista — una comprobación version = :expected (o updatedAt) donde otro escritor llegó primero.
  • Guardas de regla de negociobalance >= :amount, #status = :expected que ya no coinciden con el item almacenado.

Cómo solucionarlo

  1. Trátalo como un resultado normal, no como un fallo. Captura la excepción y decide qué significa una condición fallida en tu flujo (el item ya existe → devuélvelo; versión obsoleta → vuelve a leer y reintenta con la nueva versión).
  2. Vuelve a leer el item actual. Establece ReturnValuesOnConditionCheckFailure: 'ALL_OLD' para obtener el item que causó el fallo sin un segundo viaje de ida y vuelta — vuelve en la propia excepción (el campo Item), y no se consume capacidad de lectura.
  3. Vuelve a leer + recalcula para la concurrencia, y luego reintenta con la versión fresca — no reenvíes simplemente el mismo valor esperado.

Ese bucle de releer y comparar es exactamente lo que hace el área de preparación de DynoTable con las ediciones manuales — prepara tus escrituras y, ante un conflicto de bloqueo optimista, te muestra el Item actual junto a tu cambio para que lo resuelvas antes de que se envíe nada.

Ejemplo

import {DynamoDBClient} from '@aws-sdk/client-dynamodb';
import {DynamoDBDocumentClient, PutCommand} from '@aws-sdk/lib-dynamodb';
import {ConditionalCheckFailedException} from '@aws-sdk/client-dynamodb';

const doc = DynamoDBDocumentClient.from(new DynamoDBClient({}));

try {
  await doc.send(
    new PutCommand({
      TableName: 'Users',
      Item: {pk: 'USER#1', email: 'a@b.com'},
      ConditionExpression: 'attribute_not_exists(pk)' // create-only
    })
  );
} catch (err) {
  if (err instanceof ConditionalCheckFailedException) {
    // Expected: the user already exists. Handle gracefully.
    return {alreadyExists: true};
  }
  throw err;
}

FAQ

¿Qué causa una ConditionalCheckFailedException en DynamoDB? Una escritura (PutItem, UpdateItem, DeleteItem o un item de TransactWrite) llevaba un ConditionExpression que evaluó a false contra el item actual — por ejemplo attribute_not_exists(pk) sobre una clave que ya existe, o una comprobación de versión que ya no coincide. DynamoDB rechaza la escritura y deja el item sin cambios.

¿Cómo evito que una ConditionalCheckFailedException tumbe mi aplicación? Captura la excepción y trátala como un resultado esperado, no como un fallo. Una condición fallida normalmente significa "alguien llegó primero" (concurrencia optimista) o "el item ya existe" — ramifica sobre ella en lugar de reintentar a ciegas.

Reproducirlo

Un PutItem protegido por attribute_not_exists contra una clave que sí existe:

await client.send(
  new PutItemCommand({
    TableName: 'orders',
    Item: {pk: {S: 'ORDER#1'}, sk: {S: 'META'}},
    ConditionExpression: 'attribute_not_exists(pk)'
  })
);

Salida real:

ConditionalCheckFailedException: The conditional request failed
HTTP 400

El mensaje es deliberadamente poco informativo — nunca dice qué parte de la condición falló, ni qué contenía realmente el item. Pasa ReturnValuesOnConditionCheckFailure: "ALL_OLD" y el item actual vuelve en error.Item, lo que convierte esto de una conjetura en un diff.

Errores relacionados

Referencias

Verificado por última vez el 2026-07-13 contra la documentación oficial de AWS enlazada arriba.

Reproducido el 2026-07-26 contra DynamoDB Local 2.x con AWS SDK for JavaScript v3.1095.0 — la salida de arriba es literal.

Trabaja con DynamoDB sin la Consola

Un cliente de escritorio rápido para DynamoDB que ejecuta el SQL real que DynamoDB no puede — JOINs, GROUP BY, agregaciones — con edición visual y un agente de IA con tus propias claves de Bedrock.

Prueba gratuita de 30 días, sin tarjeta — después, el plan Free sin límite de tiempo.