Categories
mongodb mongoose

What’s Mongoose error Cast to ObjectId failed for value XXX at path “_id”?

194

When sending a request to /customers/41224d776a326fb40f000001 and a document with _id 41224d776a326fb40f000001 does not exist, doc is null and I’m returning a 404:

  Controller.prototype.show = function(id, res) {
    this.model.findById(id, function(err, doc) {
      if (err) {
        throw err;
      }
      if (!doc) {
        res.send(404);
      }
      return res.send(doc);
    });
  };

However, when _id does not match what Mongoose expects as “format” (I suppose) for example with GET /customers/foo a strange error is returned:

CastError: Cast to ObjectId failed for value “foo” at path “_id”.

So what’s this error?

0

    250

    Mongoose’s findById method casts the id parameter to the type of the model’s _id field so that it can properly query for the matching doc. This is an ObjectId but "foo" is not a valid ObjectId so the cast fails.

    This doesn’t happen with 41224d776a326fb40f000001 because that string is a valid ObjectId.

    One way to resolve this is to add a check prior to your findById call to see if id is a valid ObjectId or not like so:

    if (id.match(/^[0-9a-fA-F]{24}$/)) {
      // Yes, it's a valid ObjectId, proceed with `findById` call.
    }
    

    10

    • 7

      @Gremo You only get to pick one type to use for _id in your Mongoose schema. In the "bla" case you would use a type of String instead of the default ObjectId and you wouldn’t need to add this check as anything can be cast to a string.

      – JohnnyHK

      Feb 18, 2013 at 17:50


    • 3

      I understand, but I’d like to avoid this check. How can I create a new ObjectId from a given string (from the GET request) for passing it to the findById method?

      – gremo

      Feb 18, 2013 at 17:58

    • 1

      @Gremo You can’t. You can only construct ObjectIds from 24 hex character strings.

      – JohnnyHK

      Feb 18, 2013 at 18:24

    • 2

      You can just use find({_id: yourId},…) to query for the document with that (unique) id. That, and JohnnyHK’s answer to add _id to your schema (with your desired ‘string’ type) is the full solution to your problem.

      Jan 3, 2014 at 21:26

    • 3

      These days, 12 character strings can also be cast to an ObjectId. ObjectId("000000000000") --> 303030303030303030303030

      – Dan Ross

      Oct 15, 2016 at 10:24

    77

    Use existing functions for checking ObjectID.

    var mongoose = require('mongoose');
    mongoose.Types.ObjectId.isValid('your id here');
    

    2

    • 24

      Careful using that method as it has the curious behavior of treating any 12-byte string as valid. So it even returns true for your 'your id here' example. github.com/mongodb/js-bson/issues/106

      – JohnnyHK

      Feb 8, 2015 at 15:31

    • console.log(“here”); let i = new mongoose.Types.ObjectId(userId.id); console.log(“now here”); // this console not even printing

      Jan 22, 2018 at 15:42


    33

    I had to move my routes on top of other routes that are catching the route parameters:

    // require express and express router
    
    const express = require("express");
    const router = express.Router();
    
    // move this `/post/like` route on top
    
    router.put("/post/like", requireSignin, like);
    
    // keep the route with route parameter `/:postId` below regular routes
    
    router.get("/post/:postId", singlePost);
    

    7

    • 4

      This worked for me. I am curious to the reason behind this error. Could you please explain how moving the route below the regular routes caused the error to go away?

      – Vishwak

      Oct 4, 2019 at 13:41

    • 2

      This worked me as well. Looks like The /test/create satisfies this /test/:id with id=create. and string cannot be cast to_id.

      – kaila88

      Mar 30, 2020 at 22:02


    • 1

      @kaila88, your comment is quite logical, and the same way it is happening.

      Dec 7, 2020 at 7:55

    • 1

      This worked for me too, +1! It’s so simple once you see it, but it was driving me nuts as the exception made me think there was something wrong with my DB doc IDs. 😀

      Nov 5, 2021 at 5:49

    • 1

      @MuhammadMufeezAhmed it works because when you have params accepting route at top it satisfies criteria for other routes due to which request never reaches to the real route for example “/post/:postId” this route expects parameter after /post/, what if you try to enter /post/like It won’t reach our written /post/like route instead it will match /post/:postid and you will see unwanted behaviour that’s why always key route with parameters at bottom, so that it don’t disturb regular routes I hope answers your question

      Aug 4 at 6:09