Search code examples
node.jsmongodbvalidationmongoosesubdocument

Mongoose ValidationError Path required that is out of subdocument array bounds


This error is super wacky. It doesn't always fail, but when it does it looks like this.

I have some code that changes the "code" (a one character string) of elements in a subdocument array. It goes through each goal, checks to see if there's a change to be applied, and if so, applies it.

for (i = 0; i < user.goals.length; i++) {
  if (transformsMap[user.goals[i].code]) {
    user.goals[i].code = transformsMap[user.goals[i].code]
  }
}
user.goals.sort(function (a,b) {return a.code.charCodeAt(0) - b.code.charCodeAt(0))

When I save it, sometimes I get an error like this:

'goals.3.code': 
  { [ValidatorError: Path `code` is required.]

...but 3 in this case, is the length of the goals array. ie there is no goals.3 subdocument. I've tried logging user.goals and user.goals.length right before validation and they both agree that there are only 3 elements in the array.

I am totally baffled.


Solution

  • I'm sure I sorted out a temporary solution at the time. Finally, 7 years later, I've actually figured out what's going on with these sorts of situations.

    I had some different code today, where I would pull an object out of one array, modify it, then put it in a different array:

    const goal = user.goals.id(goalId)
    user.goals.pull(goalId)
    goal.code = goal.code + ' DELETED at ' + new Date().toISOString()
    user.goalsDeleted.push(goal)
    

    ...and I would check before saving, and user.goals would be [], but then I'd get [ValidationError] User validation failed: goals.0.code: Path goals.0.code is required

    but there WAS no goals.0.code. And I don't totally understand what's going on there, but I narrowed it down to the goal.code = ... line. My guess is that when I do that, it half-recreates goals.0 somehow. Frankly I think this is a mongo bug, but in the meantime my solution is to destroy the mongo object between pulling it off one array and putting it on the other array:

    so now my first line looks like this:

    const goal = user.goals.id(goalId).toObject()
    

    TL;DR = don't modify a subdocument after you've removed it from an array (which I believe was also involved in the above code)