Provided list of item keys contains duplicates (BatchWriteItem)

TL;DR — 你的 BatchWriteItem 请求里有两个或更多操作作用在同一个主键上(同一个键被 put 两次,或者对它既 put 又 delete)。单个批次不能对同一个项目动手超过一次,所以 DynamoDB 会拒绝整个批次。发送之前先把重复项合并成每个键一个操作。

含义

ValidationException: Provided list of item keys contains duplicates

BatchWriteItem 最多执行 25 个 put/delete 操作。请求中的每个操作都必须指向不同的主键。如果两个 PutRequest/DeleteRequest 条目解析到同一个键(分区键,或分区键 + 排序键),DynamoDB 会以这个 HTTP 400 ValidationException 让整次调用失败——什么都不会被写入。它原样不可重试

为什么会发生

  • 同一个项目在一个批次里被 put 了两次——常见于源列表本身有重复记录,而你把每条都映射成了一个 PutRequest
  • 同一个批次里对同一个键既 put 又 delete——那仍然是对一个项目的两个操作,同样会被拒绝。
  • 构建批次时没有去重——事件流里重复出现了同一个键。
  • 复合键疏忽——你以为不同的两行,其实共用了分区键 + 排序键。
  • ETL / 批量导入作业(Glue、自制导入器)在分块之前没有按键合并。

如何修复

  1. 合并成每个键一个操作,保留最后一次写入:
    import {marshall} from '@aws-sdk/util-dynamodb';
    
    const byKey = new Map();
    for (const item of records) {
      byKey.set(`${item.pk}#${item.sk ?? ''}`, {PutRequest: {Item: marshall(item)}});
    }
    const batch = [...byKey.values()]; // <= one op per key
  2. 如果你用 Python 的 batch_writer,就用 boto3 的 overwrite_by_pkeys——batch_writer(overwrite_by_pkeys=['pk', 'sk']) 会在新加入的项目与缓冲中的请求主键相同时丢弃后者,于是每个键都是最后一次写入胜出。
  3. 去重之后按每次 BatchWriteItem 25 个操作分块,并重试 UnprocessedItems(限流——那是另一码事)。
  4. 确实需要对同一个键先写后删?把它们拆到不同的请求里,或者用 TransactWriteItems(它同样禁止对一个项目做两个动作,但让意图更明确)。

要在跑之前先检视一次批量导入?DynoTable 桌面应用按键展示项目,于是排队进同一批次的重复键会一眼可见。

复现方法

一个包含同一个主键两次的 BatchWriteItem

await client.send(
  new BatchWriteItemCommand({
    RequestItems: {
      orders: [
        {PutRequest: {Item: {pk: {S: 'DUP'}, sk: {S: 'META'}}}},
        {PutRequest: {Item: {pk: {S: 'DUP'}, sk: {S: 'META'}}}}
      ]
    }
  })
);

实际输出:

ValidationException: Provided list of item keys contains duplicates
HTTP 400

整个批次都被拒绝——两次写入都没有落地。因为批次没有顺序保证,DynamoDB 不会替你决定两个重复项里哪一个该胜出,所以要在构建请求之前就按键去重。

相关错误

参考资料

最后核实于 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 agent。

30 天免费试用,无需信用卡 — 之后为无时间限制的免费版。