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。
如何修正
- 將它視為正常結果,而非故障。 捕捉例外並決定失敗的條件在你的流程中_代表_什麼(項目已存在 → 回傳它;版本過時 → 重新讀取並以新版本重試)。
- 讀回目前的項目。 設定
ReturnValuesOnConditionCheckFailure: 'ALL_OLD'以取得造成失敗的項目而無需第二次往返 — 它會回在例外本身上(Item欄位),且不消耗讀取容量。 - 為並行重新讀取 + 重新計算, 然後以新版本重新嘗試 — 不要只是重送相同的 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 上,讓這件事從猜測變成比對差異。
相關錯誤
- TransactionCanceledException — _交易內部_的條件失敗。
- ValidationException(總覽)
- 程式碼範例:Node.js 中的條件寫入 · Python(boto3)中的 — 可執行的 ConditionExpression 模式。
- 學習:條件運算式 · 原子計數器
參考資料
- Error handling with DynamoDB — Amazon DynamoDB Developer Guide
- PutItem — Amazon DynamoDB API Reference
- DynamoDB condition expression examples — Amazon DynamoDB Developer Guide
最後於 2026-07-13 對照上方連結的官方 AWS 文件驗證。
已於 2026-07-26 對照 DynamoDB Local 2.x 與 AWS SDK for JavaScript v3.1095.0 重現 — 上方輸出為逐字原文。