DynamoDB TransactionCanceledException

TL;DR — 你 TransactWriteItemsTransactGetItems 中的一個(或多個)項目失敗了,所以 DynamoDB 回復了_整筆_交易。真正的原因在 CancellationReasons 陣列裡 — 去讀它;每個項目的原因 Code 會精確告訴你是哪個項目、以及為什麼。

這是什麼意思

DynamoDB 的交易是全有或全無。只要任一項目的條件失敗、容量超出,或兩筆交易相撞,整筆就會被取消,什麼都不會寫入。最上層的訊息很籠統:

TransactionCanceledException: Transaction cancelled, please refer cancellation reasons for specific reasons [ConditionalCheckFailed, None, TransactionConflict]

中括號裡的清單是按位置對應的 — 你交易中的每個項目各一筆,依序排列。DynamoDB 以 HTTP 狀態碼 400 回傳這個例外,而 AWS SDK 不會自動重試它 — 要不要重試,由你的程式碼依每個原因代碼決定。

為什麼會發生(原因代碼)

  • ConditionalCheckFailed — 該項目的 ConditionExpression 求值為 false(見 ConditionalCheckFailedException)。
  • TransactionConflict — 另一筆並行交易(或寫入)正在操作同一個項目;請以退避重試。
  • ProvisionedThroughputExceeded — 該項目所在的資料表/索引容量用盡。
  • ThrottlingError — 資料表或索引(通常是隨需模式,而 DynamoDB 還在擴容中)節流了這次寫入;請以退避重試。
  • ValidationError — 該項目格式有誤(無效的參數值、文件路徑、運算元型別、大小溢位……)。
  • ItemCollectionSizeLimitExceeded — 某個 LSI 項目集合達到了 10 GB。
  • None — 該項目沒問題;失敗發生在清單中的其他地方。

這就是完整、有文件記載的代碼集合。請注意,重複的項目鍵(同一個項目被兩個動作指向)並不是取消代碼 — DynamoDB 會在一開始就以 ValidationException 拒絕該請求。

如何修正

  1. 從錯誤上讀取 CancellationReasons,而不是只看訊息。以索引把每一筆對應回你的輸入項目。
  2. 依代碼分支處理: ConditionalCheckFailed → 業務邏輯;TransactionConflictThrottlingErrorProvisionedThroughputExceeded → 以指數退避重試;ValidationError → 修正請求。
  3. 避免重複的鍵 — 單筆交易不能對同一個項目動兩次。

在手動編輯項目?DynoTable 的暫存區會把你的編輯批次成一次交易式寫入,並讓你在提交前檢視每一個項目 — 同樣的全有或全無語意,卻不必手工建構請求。

範例

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

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

try {
  await doc.send(new TransactWriteCommand({TransactItems: [/* ... */]}));
} catch (err) {
  if (err instanceof TransactionCanceledException) {
    for (const [i, reason] of (err.CancellationReasons ?? []).entries()) {
      if (reason.Code && reason.Code !== 'None') {
        console.error(`item ${i} cancelled: ${reason.Code}${reason.Message}`);
      }
    }
  }
  throw err;
}

常見問題

我的 DynamoDB 交易為什麼被取消了? TransactWriteItems/TransactGetItems 中的某個項目失敗了 — 條件檢查、輸送量/節流限制,或與另一筆並行交易衝突 — 所以 DynamoDB 回復了整筆交易,什麼都沒寫入。每個項目的原因在 CancellationReasons 陣列中。

我要怎麼找出交易中是哪個項目失敗? 讀取 TransactionCanceledException 上的 CancellationReasons 陣列。它對每個輸入項目各有一筆、順序相同;Code 不是「None」的那一筆就是造成取消的項目。

重現方式

一筆交易中的兩次寫入,第二次由一個不可能成立的條件防護。整筆交易會回復,而每個動作的判定會出現在 CancellationReasons 中 — 與 TransactItems 按位置對齊:

await client.send(
  new TransactWriteItemsCommand({
    TransactItems: [
      {Put: {TableName: 'orders', Item: {pk: {S: 'OK'}, sk: {S: 'META'}}}},
      {
        Put: {
          TableName: 'orders',
          Item: {pk: {S: 'ORDER#1'}, sk: {S: 'META'}},
          ConditionExpression: 'attribute_not_exists(pk)' // ORDER#1 already exists
        }
      }
    ]
  })
);

實際輸出:

TransactionCanceledException: Transaction cancelled, please refer cancellation reasons for specific reasons [None, ConditionalCheckFailed]
HTTP 400

error.CancellationReasons:
[
  {
    "Code": "None"
  },
  {
    "Code": "ConditionalCheckFailed",
    "Message": "The conditional request failed"
  }
]

第一個動作回報 None — 它並沒有失敗,它是因為鄰居失敗才被回復的。只有 Code 不是 None 的那一筆才指出真正的罪魁禍首,而它的索引就是問題動作在你自己的 TransactItems 陣列中的索引。

相關錯誤

參考資料

最後於 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 代理。

30 天免費試用,無需信用卡 — 之後為無時間限制的免費方案。