[Mongoose] Error: Unable to invalidate a subdocument that has not been added to an array. の解決方法

Mongoose で Error: Unable to invalidate a subdocument that has not been added to an array. が発生したときの調査方法をご紹介します。

mongoose | マングース

結論 type: ObjectId な field に不正な値があった

まず、結論から書いておくと type: ObjectId と定義している field に ObjectId 形式ではない不正な値を保存しようとしてエラーが発生していたようでした。

Mongoose エラーメッセージ

Error: Unable to invalidate a subdocument that has not been added to an array.

前提条件 Mongoose Schema とクエリ

'use strict';
const { Schema } = require('mongoose');
const { ObjectId } = Schema;

const RegiTransaction = new Schema(
  {
    transactionHead: {
      type: Schema.Types.Mixed
    },

    details: [
      {
        transactionDetail: {
          type: Schema.Types.Mixed
        },

        sku: {
          type: ObjectId,
          ref: 'Sku'
        }
      }
    ]
  }
);

以下のような update クエリを実行すると Error: Unable to invalidate a subdocument that has not been added to an array. が発生しました。

cost data = {
  details: [
    {
      transactionDetail: {
        foo: 'bar'
      },
      sku: 'Invalid ObjectId'
    }
  ]
};

await RegiTransaction.update(
  {
    _id: targetId
  },
  {
    $set: data
  },
  {
    upsert: true
  }
);

対策案 update クエリ発行前に ObjectId を validation

対策案として update クエリを実行する前に、RegiTransaction.details.sku に保存される予定の値が ObjectId 形式であることを検証する validation 処理を追加することで Mongoose エラーを起こさずに未然にエラーハンドリングできます。

以上、Mongoose で Error: Unable to invalidate a subdocument that has not been added to an array. を調査・解決した、現場からお送りしました。