You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MongoServerError排查:更新不可变字段_id触发ImmutableField错误

修复MongoDB ImmutableField错误:Performing an update on the path '_id' would modify the immutable field '_id'

问题描述

每次执行命令都会触发MongoServerError,提示修改不可变字段_id。此前命令运行正常,添加更新数组的新函数后出现该问题。

错误栈

MongoServerError: Plan executor error during findAndModify :: caused by :: Performing an update on the path '_id' would modify the immutable field '_id'

at Connection.onMessage (/app/node_modules/mongodb/lib/cmap/connection.js:230:30)
at MessageStream.<anonymous> (/app/node_modules/mongodb/lib/cmap/connection.js:61:60)
at MessageStream.emit (node:events:527:28)
at processIncomingData (/app/node_modules/mongodb/lib/cmap/message_stream.js:125:16)
at MessageStream._write (/app/node_modules/mongodb/lib/cmap/message_stream.js:33:9)
at writeOrBuffer (node:internal/streams/writable:390:12)
at _write (node:internal/streams/writable:331:10)
at MessageStream.Writable.write (node:internal/streams/writable:335:10)
at TLSSocket.ondata (node:internal/streams/readable:777:22)
at TLSSocket.emit (node:events:527:28) {
ok: 0,
code: 66,
codeName: 'ImmutableField',
'$clusterTime': {
clusterTime: new Timestamp({ t: 1678502007, i: 4 }),
signature: {
hash: new Binary(Buffer.from("6d07aad2ad2644b91f653725c0f9ffbbcd035be7", "hex"), 0),
keyId: new Long("7148511101804609538")
}
},
operationTime: new Timestamp({ t: 1678502007, i: 4 }),
[Symbol(errorLabels)]: Set(0) {}
}

新增函数

async function task(card, damage, interaction) {
  let profile = await profileModel.findOne({
    user: card.userID,
  });
  if (profile.task.length === 0) return;
  profile.task.forEach((e, i) => {
    if (e.complete !== true && e.type === "damage") {
      e.damageDone += damage;
    }
  });
  profile.markModified("task");
  await profileModel.findOneAndUpdate({ user: interaction.user.id }, profile);
  return profile;
}

核心代码片段

await profileModel.findOneAndUpdate(
              {
                user: winner,
              },
              {
                $inc: {
                  coins: bet,
                },
              }
            );
            await profileModel.findOneAndUpdate(
              {
                user: loser,
              },
              {
                $inc: {
                  coins: -bet,
                },
              }
            );
            await interaction.followUp({
              embeds: [endEmbed],
            });
            if (winner === interaction.user.id) {
              if (profile.guild) {
                let guild = await guildModel.findOne({
                  name: profile.guild,
                });
                guild.chestXP += 15;
                await profileModel.findOneAndUpdate(
                  {
                    user: interaction.user.id,
                  },
                  profile
                );
              }
              if (profile.task.length === 0) return;
              let profileTask = profile.task.filter(
                (v) =>
                  v.complete !== true && v.type === "badge" && v.id === "bc"
              );
              if (profileTask.length === 0) return;
              profileTask.forEach(async (element, int) => {
                let task = element;
                task.progress += 1;
                await profileModel.findOneAndUpdate(
                  {
                    user: interaction.user.id,
                  },
                  profile
                );
              });
            }
          }

解决方案

错误根源

通过findOne获取Mongoose文档实例后,直接将整个实例传给findOneAndUpdate,MongoDB会执行文档替换操作,此时实例中的_id字段会被当作更新字段,而_id是MongoDB的不可变字段,因此触发错误。

正确做法是:修改文档实例后直接调用save()方法,Mongoose会自动生成仅更新变化字段的操作,不会包含_id。

1. 修复新增的task函数

async function task(card, damage, interaction) {
  let profile = await profileModel.findOne({ user: card.userID });
  if (!profile || profile.task.length === 0) return;

  profile.task.forEach((e) => {
    if (e.complete !== true && e.type === "damage") {
      e.damageDone += damage;
    }
  });

  profile.markModified("task");
  await profile.save(); // 替换findOneAndUpdate,直接保存修改后的实例
  return profile;
}

2. 修复核心代码中的错误

修复 guild 相关的更新

将:

await profileModel.findOneAndUpdate(
  { user: interaction.user.id },
  profile
);

替换为:

await profile.save();

修复任务进度更新的循环

原forEach循环存在异步并发问题,且多次调用findOneAndUpdate效率低下,改为for...of循环后一次性保存:

// 替换原forEach代码块
if (profileTask.length > 0) {
  for (const element of profileTask) {
    element.progress += 1;
  }
  profile.markModified("task");
  await profile.save();
}

验证修改

修改完成后,执行命令时MongoDB只会更新实际变化的字段(如task数组),不会尝试修改_id,即可解决该错误。

内容的提问来源于stack exchange,提问作者Shinpi Tekita

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 22:35:19