Two document paths overlap with each other

TL;DR — 一个 UpdateExpression 里,每条文档路径只能碰一次,而且任何路径都不能嵌套在同一表达式碰到的另一条路径_里面_。SET profile = :p, profile.email = :e 就重叠了(profile.email 住在 profile 里面);两次点到同一个属性也一样。把子字段折进父值里,或者拆成两次更新。

含义

ValidationException: 1 validation error detected: Invalid UpdateExpression: Two document paths overlap
with each other; must remove or rewrite one of these paths;
path one: [profile], path two: [profile, email]

DynamoDB 对 UpdateExpression 里的每个动作求值时,用的都是项目更新之前的属性值——各个动作并不是从左到右依次施加的。如果两个动作指向重叠的路径(同一个属性,或者一个父路径和嵌套在它里面的东西),结果就会有歧义:profile.email = :e 到底是在 profile = :p 替换整个 map 之前跑,还是之后跑?与其去猜,DynamoDB 干脆直接拒绝这个表达式。(同样的重叠检查也适用于 ProjectionExpression 里的重复路径。)

为什么会发生

  • 在一个表达式里同时设置父与子——SET profile = :p, profile.email = :e。第二条路径在第一条里面。
  • 同一个属性出现了两次——SET updatedAt = :a REMOVE updatedAt,或者 SET tags = :t ADD tags :more
  • 某个 ODM/包装层悄悄加了一条你也在设置的路径——经典情形:某个库自动写入一个时间戳或整个对象(SET item = :obj),而你的代码同时还在设置 item.field(在 Dynamoose 的自动 createdAt/updatedAt 上见过)。
  • 同时更新一个列表和它的某个元素——SET mylist = :l, mylist[0] = :v

如何修复

  1. 把子字段折进父值里——如果你反正要替换整个 map,就把新的 email 放进去,并去掉第二个动作:

    // instead of SET profile = :p, profile.email = :e
    UpdateExpression: 'SET #p = :p',
    ExpressionAttributeValues: {':p': {name: 'Ada', email: 'ada@example.com'}}
  2. 或者只更新叶子节点——别动父节点,逐个设置嵌套字段(SET #p.#n = :n, #p.#e = :e)。同一父节点下的兄弟路径不算重叠;只有嵌套才算。

  3. 去重——确保每个属性在 SET/REMOVE/ADD/DELETE 中恰好只出现在一个动作里。

  4. 检查你所用库的自动字段——在你自己的表达式已经写了它们的更新里,禁用或排除那些自动管理的属性(时间戳、版本号)。

  5. 当你确实需要“先替换父、再微调子”的语义时,就拆成两个请求——两次 UpdateItem 调用,按顺序发。

手工编辑嵌套 map 正是重叠溜进来的地方——DynoTable 桌面应用就地编辑项目的属性,并只针对真正改动的部分发出一次干净、不重叠的更新。

复现方法

一个 UpdateExpression 同时设置了一个 map 和那个 map 里面的一个字段:

await client.send(
  new UpdateItemCommand({
    TableName: 'orders',
    Key: {pk: {S: 'ORDER#1'}, sk: {S: 'META'}},
    UpdateExpression: 'SET #a = :v, #a.#b = :w',
    ExpressionAttributeNames: {'#a': 'addr', '#b': 'city'},
    ExpressionAttributeValues: {':v': {M: {}}, ':w': {S: 'Berlin'}}
  })
);

实际输出:

ValidationException: 1 validation error detected: Invalid UpdateExpression: Two document paths overlap with each other; must remove or rewrite one of these paths; path one: [addr], path two: [addr, city]
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 天免费试用,无需信用卡 — 之后为无时间限制的免费版。