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. 為並行重新讀取 + 重新計算, 然後以新版本重新嘗試 — 不要只是重送相同的 expected 值。

這種「重新讀取再比較」的迴圈,正是 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,它對目前的項目求值為 false — 例如對一個已存在的鍵使用 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 代理。

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