What's Mongoose error Cast to ObjectId failed for value XXX at path "_id"?

MongodbMongoose

Mongodb Problem Overview


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?

Mongodb Solutions


Solution 1 - Mongodb

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.
}

Solution 2 - Mongodb

Use existing functions for checking ObjectID.

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

Solution 3 - Mongodb

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);

Solution 4 - Mongodb

I have the same issue I add
_id: String .in schema then it start work

Solution 5 - Mongodb

This might be a case of routes mismatch if you have two different routes like this

router.route("/order/me") //should come before the route which has been passed with params
router.route("/order/:id")

then you have to be careful putting the route that is using a param after the regular route that worked for me

Solution 6 - Mongodb

Are you parsing that string as ObjectId?

Here in my application, what I do is:

ObjectId.fromString( myObjectIdString );

Solution 7 - Mongodb

it happens when you pass an invalid id to mongoose. so first check it before proceeding, using mongoose isValid function

import mongoose from "mongoose";

// add this inside your route
if( !mongoose.Types.ObjectId.isValid(id) ) return false;

Solution 8 - Mongodb

In my case, I had to add _id: Object into my Schema, and then everything worked fine.

Solution 9 - Mongodb

 if(mongoose.Types.ObjectId.isValid(userId.id)) {
        User.findById(userId.id,function (err, doc) {
            if(err) {
                reject(err);
            } else if(doc) {
                resolve({success:true,data:doc});
            } else {
                reject({success:false,data:"no data exist for this id"})
                
            }
        });
        } else {
            reject({success:"false",data:"Please provide correct id"});
        }

best is to check validity

Solution 10 - Mongodb

As of Nov 19, 2019

You can use isValidObjectId(id) from mongoose version 5.7.12

https://mongoosejs.com/docs/api/mongoose.html#mongoose_Mongoose-isValidObjectId

Solution 11 - Mongodb

You can also use ObjectId.isValid like the following :

if (!ObjectId.isValid(userId)) return Error({ status: 422 })

Solution 12 - Mongodb

If above solutions do not work for you. Check if you are sending a GET request to a POST route.
It was that simple and stupid for me.

Solution 13 - Mongodb

I was faced with something similar recently and solved it by catching the error to find out if it's a Mongoose ObjectId error.

app.get("/:userId", (req, res, next) => {
	try {
		// query and other code here
	} catch (err) {
		if (err.kind === "ObjectId") {
			return res.status(404).json({
				errors: [
					{
						msg: "User not found",
						status: "404",
					},
				],
			});
		}
		next(err);
	}
});

Solution 14 - Mongodb

You could either validate every ID before using it in your queries (which I think is the best practice),

// Assuming you are using Express, this can return 404 automatically.
app.post('/resource/:id([0-9a-f]{24})', function(req, res){
  const id = req.params.id;
  // ...
});

... or you could monkey patch Mongoose to ignore those casting errors and instead use a string representation to carry on the query. Your query will of course not find anything, but that is probably what you want to have happened anyway.

import { SchemaType }  from 'mongoose';

let patched = false;

export const queryObjectIdCastErrorHandler = {
  install,
};

/**
 * Monkey patches `mongoose.SchemaType.prototype.castForQueryWrapper` to catch
 * ObjectId cast errors and return string instead so that the query can continue
 * the execution. Since failed casts will now use a string instead of ObjectId
 * your queries will not find what they are looking for and may actually find
 * something else if you happen to have a document with this id using string
 * representation. I think this is more or less how MySQL would behave if you
 * queried a document by id and sent a string instead of a number for example.
 */
function install() {
  if (patched) {
    return;
  }

  patch();

  patched = true;
}

function patch() {
  // @ts-ignore using private api.
  const original = SchemaType.prototype.castForQueryWrapper;

  // @ts-ignore using private api.
  SchemaType.prototype.castForQueryWrapper = function () {
    try {
      return original.apply(this, arguments);
    } catch (e) {
      if ((e.message as string).startsWith('Cast to ObjectId failed')) {
        return arguments[0].val;
      }

      throw e;
    }
  };
}

Solution 15 - Mongodb

All you have to do is change the parameter name "id" to "_id"

Solution 16 - Mongodb

I went with an adaptation of the @gustavohenke solution, implementing cast ObjectId in a try-catch wrapped around the original code to leverage the failure of ObjectId casting as a validation method.

Controller.prototype.show = function(id, res) {
  try {
    var _id = mongoose.Types.ObjectId.fromString(id);



    // the original code stays the same, with _id instead of id:

    this.model.findById(_id, function(err, doc) {
      if (err) {
        throw err;
      }
      if (!doc) {
        res.send(404);
      }
      return res.send(doc);
    });



  } catch (err) {
    res.json(404, err);
  }
};

Solution 17 - Mongodb

This is an old question but you can also use express-validator package to check request params

express-validator version 4 (latest):

validator = require('express-validator/check');

app.get('/show/:id', [

    validator.param('id').isMongoId().trim()

], function(req, res) {

    // validation result
    var errors = validator.validationResult(req);

    // check if there are errors
    if ( !errors.isEmpty() ) {
        return res.send('404');
    }
        
    // else 
    model.findById(req.params.id, function(err, doc) { 
        return res.send(doc);
    });

});

express-validator version 3:

var expressValidator = require('express-validator');
app.use(expressValidator(middlewareOptions));

app.get('/show/:id', function(req, res, next) {
    
    req.checkParams('id').isMongoId();

    // validation result
    req.getValidationResult().then(function(result) {

        // check if there are errors
        if ( !result.isEmpty() ) {
            return res.send('404');
        }

        // else
        model.findById(req.params.id, function(err, doc) {
            return res.send(doc);
        });

    });

});

Solution 18 - Mongodb

Always use mongoose.Types.ObjectId('your id')for conditions in your query it will validate the id field before running your query as a result your app will not crash.

Solution 19 - Mongodb

ObjectId is composed of following things.

  1. a 4-byte value representing the seconds since the Unix epoch
  2. a 5-byte random value (Machine ID 3 bytes and Processor id 2 bytes)
  3. a 3-byte counter, starting with a random value.

Correct way to validate if the objectId is valid is by using static method from ObjectId class itself.

mongoose.Types.ObjectId.isValid(sample_object_id)

Solution 20 - Mongodb

//Use following to check if the id is a valid ObjectId?

var valid = mongoose.Types.ObjectId.isValid(req.params.id);
if(valid)
{
  //process your code here
} else {
  //the id is not a valid ObjectId
}

Solution 21 - Mongodb

I was having problems with this and fixed doing mongoose.ObjectId(id) without Types

Solution 22 - Mongodb

In my case, similar routes caused this problem.

Router.get("/:id", getUserById);
Router.get("/myBookings",getMyBookings);

In above code, whenever a get request to route "/myBookings" is made, it goes to the first route where req.params.id is equals to "myBookings" which is not a valid ObjectId.

It can be corrected by making path of both routes different.

Something like this

Router.get("/user/:id", getUserById);
Router.get("/myBookings",getMyBookings);

Solution 23 - Mongodb

OR you can do this

var ObjectId = require('mongoose').Types.ObjectId; var objId = new ObjectId( (param.length < 12) ? "123456789012" : param );

as mentioned here Mongoose's find method with $or condition does not work properly

Solution 24 - Mongodb

Cast string to ObjectId

import mongoose from "mongoose"; // ES6 or above
const mongoose = require('mongoose'); // ES5 or below

let userid = _id
console.log(mongoose.Types.ObjectId(userid)) //5c516fae4e6a1c1cfce18d77

Solution 25 - Mongodb

The way i fix this problem is transforming the id into a string

i like it fancy with backtick: `${id}`

this should fix the problem with no overhead

Solution 26 - Mongodb

Detecting and Correcting the ObjectID Error

I stumbled into this problem when trying to delete an item using mongoose and got the same error. After looking over the return string, I found there were some extra spaces inside the returned string which caused the error for me. So, I applied a few of the answers provided here to detect the erroneous id then remove the extra spaces from the string. Here is the code that worked for me to finally resolve the issue.

const mongoose = require("mongoose");
mongoose.set('useFindAndModify', false);  //was set due to DeprecationWarning: Mongoose: `findOneAndUpdate()` and `findOneAndDelete()` without the `useFindAndModify`



app.post("/delete", function(req, res){
  let checkedItem = req.body.deleteItem;
  if (!mongoose.Types.ObjectId.isValid(checkedItem)) {
    checkedItem = checkedItem.replace(/\s/g, '');
  }

  Item.findByIdAndRemove(checkedItem, function(err) {
    if (!err) {
      console.log("Successfully Deleted " + checkedItem);
        res.redirect("/");
      }
    });
});

This worked for me and I assume if other items start to appear in the return string they can be removed in a similar way.

I hope this helps.

Solution 27 - Mongodb

I had the same error, but in a different situation than in the question, but maybe it will be useful to someone.

The problem was adding buckles:

Wrong:

    const gamesArray = [myId];

    const player = await Player.findByIdAndUpdate(req.player._id, {
         gamesId: [gamesArray]
    }, { new: true }

Correct:

    const gamesArray = [myId];

    const player = await Player.findByIdAndUpdate(req.player._id, {
         gamesId: gamesArray
    }, { new: true }

Solution 28 - Mongodb

In my case the parameter id length was 25, So I trimmed first character of parameter id and tried. It worked.

> Blockquote

const paramId = req.params.id;
if(paramId.length === 25){
  const _id = paramId.substring(1, 25);
}

To change the string object to ObjectId instance fromString() method is not exist anymore. There is a new method createFromHexString().

const _id = mongoose.Types.ObjectId.fromString(id); // old method not available
const _id = mongoose.Types.ObjectId.createFromHexString(id); // new method.

Solution 29 - Mongodb

could happen if you are sending less or more then 24 characters string as id

Solution 30 - Mongodb

I fixed this problem changing the order of the routes.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiongremoView Question on Stackoverflow
Solution 1 - MongodbJohnnyHKView Answer on Stackoverflow
Solution 2 - MongodbxpepermintView Answer on Stackoverflow
Solution 3 - MongodbRyan DhungelView Answer on Stackoverflow
Solution 4 - Mongodbs.babarView Answer on Stackoverflow
Solution 5 - MongodbHarsh VermaView Answer on Stackoverflow
Solution 6 - MongodbgustavohenkeView Answer on Stackoverflow
Solution 7 - MongodbEricgitView Answer on Stackoverflow
Solution 8 - MongodbCrowdpleasrView Answer on Stackoverflow
Solution 9 - Mongodbyogesh agrawalView Answer on Stackoverflow
Solution 10 - Mongodbthink-seriousView Answer on Stackoverflow
Solution 11 - MongodbZEEView Answer on Stackoverflow
Solution 12 - MongodbManil MallaView Answer on Stackoverflow
Solution 13 - MongodbEronsView Answer on Stackoverflow
Solution 14 - MongodbDenis PshenovView Answer on Stackoverflow
Solution 15 - MongodbIheb SaadView Answer on Stackoverflow
Solution 16 - MongodbCharney KayeView Answer on Stackoverflow
Solution 17 - MongodbYouneLView Answer on Stackoverflow
Solution 18 - MongodbSumanView Answer on Stackoverflow
Solution 19 - MongodbSushil KaduView Answer on Stackoverflow
Solution 20 - MongodbBrajalal PalView Answer on Stackoverflow
Solution 21 - MongodbJuanyView Answer on Stackoverflow
Solution 22 - MongodbAshutosh SainiView Answer on Stackoverflow
Solution 23 - MongodbMadhu KumarView Answer on Stackoverflow
Solution 24 - MongodbWasiFView Answer on Stackoverflow
Solution 25 - MongodbionView Answer on Stackoverflow
Solution 26 - MongodbJim BrayView Answer on Stackoverflow
Solution 27 - MongodbProvokator555View Answer on Stackoverflow
Solution 28 - MongodbStanley LambertView Answer on Stackoverflow
Solution 29 - MongodbarisView Answer on Stackoverflow
Solution 30 - MongodbidionisioView Answer on Stackoverflow