DynamoDB ConditionalCheckFailedException

TL;DR — 你的写入带了一个 ConditionExpression,它针对当前项目求值为 false,因此 DynamoDB 拒绝了该写入并保持项目不变。这通常是_预期之内_的(乐观并发、“不存在则创建”)——捕获它并分支处理,不要盲目重试。

含义

ValidationException 不同,请求本身格式良好。DynamoDB 对你的条件求了值,而它不成立,因此 PutItem / UpdateItem / DeleteItem(或 TransactWriteItems 内的单个项目)被拒绝了。没有数据被改变。它返回 HTTP 400,且原样不可重试

为什么会发生

  • 创建时的 attribute_not_exists(pk) 守卫——项目已经存在(重复插入)。
  • 更新/删除时的 attribute_exists(pk) 守卫——项目已经不在了。
  • 乐观并发——一个 version = :expected(或 updatedAt)检查,其中另一个写入者先到了一步。
  • 业务规则守卫——balance >= :amount#status = :expected 与存储的项目不再匹配。

如何修复

  1. 把它当作正常结果,而非故障。捕获异常,判断一个失败的条件在你的流程中_意味着_什么(项目已存在 → 返回它;版本陈旧 → 重新读取并用新版本重试)。
  2. 把当前项目读回来。设置 ReturnValuesOnConditionCheckFailure: 'ALL_OLD',无需第二次往返即可拿到导致失败的那个项目——它随异常本身返回(Item 字段),且不消耗任何读取容量。
  3. 对并发情况重新读取 + 重新计算,然后用新版本重新尝试——不要只是把同一个期望值再发一遍。

那套“重新读取再比对”的循环,正是 DynoTable 的暂存区为手工编辑所做的事——它把你的写入暂存起来,一旦发生乐观锁冲突,就把当前项目摆在你的改动旁边,让你在任何东西被发送出去之前解决冲突。

示例

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;
}

常见问题

是什么导致了 DynamoDB 中的 ConditionalCheckFailedException? 一次写入(PutItem、UpdateItem、DeleteItem 或 TransactWrite 中的一项)带了一个 ConditionExpression,它针对当前项目求值为假——例如对一个已经存在的键使用 attribute_not_exists(pk),或者一个不再匹配的版本检查。DynamoDB 拒绝该写入并保持项目不变。

我怎样才能不让 ConditionalCheckFailedException 崩溃我的应用? 捕获该异常,把它当作预期的结果而非故障。一个失败的条件通常意味着“别人先到了一步”(乐观并发)或“项目已经存在”——对它分支处理,而不是盲目重试。

复现方法

一个由 attribute_not_exists 守卫、却打在一个确实存在的键上的 PutItem

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

实际输出:

ConditionalCheckFailedException: The conditional request failed
HTTP 400

这条消息是刻意不提供信息的——它从不说是条件的哪一部分失败了,也不说项目里实际存的是什么。传入 ReturnValuesOnConditionCheckFailure: "ALL_OLD",当前项目就会随 error.Item 一起回来,于是这件事从猜测变成了对比。

相关错误

参考资料

最后核实于 2026-07-13,依据上方链接的 AWS 官方文档。

2026-07-26 针对 DynamoDB Local 2.x 与 AWS SDK for JavaScript v3.1095.0 复现——上方输出为原样照录。

无需控制台即可使用 DynamoDB

一款快速的 DynamoDB 桌面客户端,可运行 DynamoDB 无法执行的真正 SQL——JOINs、GROUP BY、聚合——并支持可视化编辑和运行在你自己的 Bedrock 密钥上的 AI agent。

30 天免费试用,无需信用卡 — 之后为无时间限制的免费版。