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

MongoDB执行findOneAndUpdate更新文档报_id不可修改错误

问题场景

我搭建了用于管理MongoDB数据库中所有集合的管理员门户,目前正在开发文档更新功能,需求为根据用户名匹配已存在的用户,将管理员提交的修改内容更新到对应用户文档。
为此在控制器中编写了如下更新接口:

exports.updateUser = async(req, res) => {
  try{
    var user = new User({
      firstName: req.body.firstName,
      lastName: req.body.lastName,
      username:req.body.username,
      password: req.body.password,
      email: req.body.email
    });
    const static_username = req.body.static_username;

    await User.findOneAndUpdate({static_username} , user, { useFindAndModify: false})
    .then(data => {
        if(!data){
          res.status(404).send({ message : `Cannot Update user with ${id}. Maybe user not found!`})
        }else{
          res.redirect('/admin');
        }
    })
    .catch(err =>{
        res.status(500).send({ message : "Error Update user information"})
    })
  } catch {
    console.log(err);
  }
}

接口设计逻辑:

  • 从请求体中提取管理员提交的修改字段存入user实例对象
  • 将更新前的用户名赋值给static_username变量,作为查询待更新用户的匹配条件

接口运行异常表现:
执行时进入了findOneAndUpdate的catch分支,返回如下响应:

{ "message" : "Error Update user information"}

底层抛出的原始错误信息:

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

故障原因

报错的核心原因是传给findOneAndUpdate的更新参数是Mongoose模型的全新实例,而非普通的更新字段对象:
当你通过new User()创建模型实例时,Mongoose会自动为这个实例生成一个全新的_id属性(即使你没有手动传入该字段)。将这个完整的模型实例作为更新内容提交给MongoDB时,数据库会尝试用新实例的_id覆盖原文档的_id值,但_id是MongoDB的不可变字段,文档写入后就不允许修改,因此直接抛出字段不可修改的错误。

除此之外代码还存在三个潜在问题:

  • 404响应分支中使用了未定义的id变量,触发用户不存在场景时会抛出引用错误
  • 混用async/await和.then()/.catch()语法,外层catch块未声明接收err参数,异常发生时日志打印会报错
  • 查询条件{static_username}是ES6简写语法,等价于{static_username: static_username},即匹配集合中static_username字段等于传入值的文档,如果你的用户集合中没有名为static_username的字段,即使修复_id问题也会无法匹配到目标文档。

修复方法

不要给更新接口传全新的Mongoose模型实例,直接组装普通对象作为更新参数即可,参考修改代码:

exports.updateUser = async(req, res) => {
  try{
    // 直接组装更新字段的普通对象,不创建User模型实例
    const updateFields = {
      firstName: req.body.firstName,
      lastName: req.body.lastName,
      username: req.body.username,
      password: req.body.password,
      email: req.body.email
    };
    const static_username = req.body.static_username;

    // 注意核对查询条件,如果你是用username字段匹配旧用户名,需要写成{username: static_username}
    const updatedUser = await User.findOneAndUpdate(
      { username: static_username },
      updateFields,
      { useFindAndModify: false, new: true }
    )

    if(!updatedUser){
      return res.status(404).send({ message : `Cannot Update user with username ${static_username}. Maybe user not found!`})
    }
    res.redirect('/admin');
  } catch (err) {
    console.log(err);
    res.status(500).send({ message : "Error Update user information"})
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 04:36:23