DynamoDB TransactionCanceledException — ConditionalCheckFailed

TL;DR — 你的 TransactWriteItems 被取消,且 CancellationReasons 陣列中含有一個 ConditionalCheckFailed 項目。交易的某個 condition expression 未成立,所以 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 求值為 false,DynamoDB 就取消整個請求,並回報一份逐項目的原因列表。位置 iConditionalCheckFailed 表示第 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 的項目指出其 condition expression 求值為 false 的動作;代碼為 None 的項目成功了。

交易內的 ConditionalCheckFailed 可以重試嗎? 不會自動重試。它是真正的前置條件衝突,而非暫時性錯誤。重新讀取該項目、決定該寫入是否仍適用,並以新的條件重試 — 或向使用者浮現衝突。

相關錯誤

References

最後驗證於 2026-07-13,對照上方連結的 AWS 官方文件。

不必透過主控台就能操作 DynamoDB

DynoTable 是一款快速的 DynamoDB 桌面用戶端 — 瀏覽表格、執行 SQL 風格的查詢,並在本機編輯項目。