DynamoDB TransactionCanceledException
TL;DR — 你 TransactWriteItems / TransactGetItems 里有一个(或多个)项目失败了,于是 DynamoDB 回滚了_整个_事务。真正的原因在 CancellationReasons 数组里——去读它;每个项目的原因 Code 会准确告诉你是哪个项目、以及为什么。
含义
DynamoDB 的事务是全有或全无的。只要任何一个项目的条件不成立、容量超了,或者两个事务撞在一起,整件事就会被取消,什么都不会写入。顶层的消息是笼统的:
TransactionCanceledException: Transaction cancelled, please refer cancellation reasons for specific reasons [ConditionalCheckFailed, None, TransactionConflict]方括号里的列表是按位置对应的——你事务里每个项目一项,顺序一致。DynamoDB 以 HTTP 状态码 400 返回这个异常,而且 AWS SDK 不会自动重试它——由你的代码按原因代码逐个决定重试是否说得通。
为什么会发生(原因代码)
ConditionalCheckFailed——那个项目的ConditionExpression求值为假(见 ConditionalCheckFailedException)。TransactionConflict——另一个并发事务(或写入)正在操作同一个项目;带退避重试。ProvisionedThroughputExceeded——该项目所在的表/索引容量用光了。ThrottlingError——表或索引限流了这次写入(通常是按需模式,而 DynamoDB 还在给它扩容);带退避重试。ValidationError——那个项目本身格式不对(参数值、文档路径、操作数类型、大小溢出……)。ItemCollectionSizeLimitExceeded——某个 LSI 项目集合撞上了 10 GB。None——那个项目本身没问题;失败发生在列表里的别处。
以上就是完整的、有文档记载的代码集合。注意,主键重复(同一个项目被两个动作指向)并不是一个取消原因代码——DynamoDB 会在一开始就把那种请求作为 ValidationException 拒掉。
如何修复
- 从错误上读
CancellationReasons,而不是只看消息。按下标把每一项映射回你的输入项目。 - 按代码分支处理:
ConditionalCheckFailed→ 业务逻辑;TransactionConflict/ThrottlingError/ProvisionedThroughputExceeded→ 带指数退避重试;ValidationError→ 修请求。 - 避免重复的键——单个事务不能对同一个项目动两次。
在手工编辑项目?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 数组里的下标。
相关错误
- ConditionalCheckFailedException
- ProvisionedThroughputExceededException
- 代码示例:TransactWriteItems in Node.js · in Python (boto3)——一个可运行的事务,供你对照。
- 学习:DynamoDB transactions
参考资料
- TransactWriteItems — Amazon DynamoDB API Reference
- TransactGetItems — Amazon DynamoDB API Reference
- Amazon DynamoDB Transactions: How it works — Amazon DynamoDB Developer Guide
- Error handling with DynamoDB — Amazon DynamoDB Developer Guide
最后核实于 2026-07-13,依据上方链接的 AWS 官方文档。
2026-07-26 针对 DynamoDB Local 2.x 与 AWS SDK for JavaScript v3.1095.0 复现——上方输出为原样照录。