DynamoDB TransactionCanceledException — ConditionalCheckFailed

TL;DR — 你的 TransactWriteItems 被取消了,CancellationReasons 数组中有一个 ConditionalCheckFailed 条目。事务的某个条件表达式不成立,因此 DynamoDB 原子性地回滚了每一个操作。读取 reasons 数组——它的条目按位置排列,每个请求项目一个——找出哪个项目的条件失败了,然后修复该项目的前置条件或数据。

含义

TransactionCanceledException: Transaction cancelled, please refer cancellation
reasons for specific reasons [ConditionalCheckFailed, None, None]
# CancellationReasons[0] = { Code: "ConditionalCheckFailed",
#                            Message: "The conditional request failed" }

TransactWriteItems 是全有或全无的。如果任何单个操作的 ConditionExpression 求值为假,DynamoDB 就会取消整个请求并报告一份逐项目的原因列表。位置 i 处的 ConditionalCheckFailed 意味着第 i 个项目的条件未被满足——事务从未部分应用。

为什么会发生

  • 乐观锁未命中——一个 version = :v / attribute_not_exists(pk) 守卫失败了,因为另一个写入者已经更改或创建了该项目。
  • 唯一性守卫被触发——一个 attribute_not_exists(pk) 插入在竞速中落败,因此该项目已经存在。
  • 陈旧读取——条件是基于一个此后已改变的值构建的。
  • 误读原因列表——数组是按位置排列的,顺序与你的 TransactItems 一致;某个槽位的 None 表示那个项目没问题,ConditionalCheckFailed 标记的是失败的那个。(数组中的其他代码——TransactionConflictItemCollectionSizeLimitExceededProvisionedThroughputExceededThrottlingErrorValidationError——含义各不相同。)

如何修复

  1. 检查异常中的 CancellationReasons,找到带 ConditionalCheckFailed 的索引——那就是失败的操作。
  2. 重新读取该项目并决定:用一个新的前置条件重试(乐观锁重试循环),或者向调用方呈现一个冲突。
  3. 修复条件(如果它是错的)——例如对一个合法地已经存在的项目使用 attribute_not_exists(pk)
  4. 给失败的操作添加 ReturnValuesOnConditionCheckFailure: ALL_OLD,让 DynamoDB 返回打破条件的那个项目(对调试很有用)。
  5. 限制你的重试次数——一个持续失败的条件是真实的业务冲突,而非瞬时错误;不要无限重试。

在手工调试一个失败的事务?在重试之前,先在 DynoTable 桌面应用中检查当前的项目状态,看清前置条件为何不成立。

FAQ

我怎么知道事务中的哪个项目失败了? 读取 TransactionCanceledException 上的 CancellationReasons 数组。它是按位置排列的——每个请求项目一个条目,顺序一致。代码为 ConditionalCheckFailed 的条目标识了其条件表达式求值为假的那个操作;代码为 None 的条目成功了。

事务内部的 ConditionalCheckFailed 可以重试吗? 不能自动重试。它是一个真实的前置条件冲突,而非瞬时错误。重新读取该项目,判断该写入是否仍然适用,然后用一个新的条件重试——或者把冲突呈现给用户。

相关错误

References

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

无需控制台即可使用 DynamoDB

DynoTable 是面向 DynamoDB 的快速桌面客户端 —— 浏览表、运行 SQL 风格的查询,并在本地编辑项。