Download

BSONError: input must be a 24 character hex string, 12 byte Uint8Array, or an integer

You passed something to ObjectId() that isn’t 24 hexadecimal characters, so the driver couldn’t build an id and nothing was sent to the server. Check the value with ObjectId.isValid before converting it, and answer a bad id with an error of your own.

MongoDB error· Tested on mongosh 2.12.0; Node.js driver 6.20.0 (bson 6.10.4); MongoDB 8.0.32· Updated 11 October 2026

input must be a 24 character hex string, 12 byte Uint8Array, or an integer

What it means

An ObjectId is 12 bytes, written as 24 hexadecimal characters (0–9, a–f), for example 65f1c2e9a1b2c3d4e5f6a7b8. new ObjectId(value) (and ObjectId(value) in mongosh) builds one from a 24-character hex string, 12 raw bytes, or an integer. Given a string that isn’t 24 hex characters, the bson library that mongosh and the Node.js driver use throws BSONError. It happens in your code, before a query is sent, so the server never sees it.

Older versions of the library said it differently, as a BSONTypeError: Argument passed in must be a string of 12 bytes or a string of 24 hex characters. It’s the same problem. ObjectId.createFromHexString() words it as hex string must be 24 characters. Mongoose wraps the same failure in Cast to ObjectId failed.

Common causes

  1. A value from a URL or form that isn’t an id: undefined, me, new, a slug, or the literal :id, passed straight to new ObjectId(req.params.id).
  2. A truncated or padded id: 23 or 25 characters, or a space or newline at either end.
  3. Characters that aren’t hex: an o instead of a 0, or an id copied with its quotes or with the ObjectId('…') around it.
  4. Ids that aren’t ObjectIds at all. The collection’s _ids are strings or numbers, and the code converts every id it gets.

How to fix it

Check the value before converting it

const { ObjectId } = require("mongodb")

app.get("/orders/:id", async (req, res) => {
  const id = req.params.id.trim()
  if (!/^[0-9a-f]{24}$/i.test(id)) {
    return res.status(400).json({ error: "Invalid order id" })
  }
  const order = await db.collection("orders").findOne({ _id: new ObjectId(id) })
  if (!order) return res.status(404).end()
  res.json(order)
})

The pattern accepts exactly 24 hex digits, in either case. ObjectId.isValid(id) does much the same for strings, but it also returns true for a number, so it says less about a value whose type you don’t know.

Find the bad value

Print it with JSON.stringify so spaces and newlines show, and check its length. If it came from a URL, check the code that built the URL: /orders/undefined means a variable wasn’t set.

Keep string ids as strings

If the collection’s _ids aren’t ObjectIds, don’t convert them. Ask the server which types it holds:

db.orders.aggregate([{ $group: { _id: { $type: "$_id" }, n: { $sum: 1 } } }])

Then query with the type you get. A string never matches an ObjectId, even with the same hex digits: find({ _id: "65f1c2e9a1b2c3d4e5f6a7b8" }) finds nothing when the stored _id is ObjectId("65f1c2e9a1b2c3d4e5f6a7b8").

Watch for ObjectId(undefined)

new ObjectId(undefined) doesn’t throw: it makes a brand-new id, so a query with it quietly matches nothing. If the value can be missing, check for that before converting.

Reproduce it

mongosh 2.12.0, connected to MongoDB 8.0.32:

ObjectId("abc")
BSONError: input must be a 24 character hex string, 12 byte Uint8Array, or an integer

A 23-character id and an id with a trailing space failed the same way; after .trim() the second worked. ObjectId.createFromHexString("abc") gave BSONError: hex string must be 24 characters. ObjectId.isValid returned true for the 24-character id and false for "hello" and for a 12-character string.

From a Node.js script with the driver 6.20.0 (bson 6.10.4), new ObjectId(v) threw the same BSONError for "abc", a 23-character id, a padded id, "zzzzzzzzzzzzzzzzzzzzzzzz" and "aaaaaaaaaaaa". An upper-case hex id was accepted and printed in lower case. undefined and null each returned a new id, and 123 an id built from the number. In a collection where a document’s _id was an ObjectId, findOne with the same id as a string returned null.

In Inlet

Queries use mongosh syntax, so db.orders.find({ _id: ObjectId("65f1c2e9a1b2c3d4e5f6a7b8") }) works as it does in mongosh. Collections show as tables with each document in the inspector, so you can see whether an _id is an ObjectId or a string before you query it.

Inlet: a database client for the Mac

One native app for PostgreSQL, MySQL, SQL Server, SQLite, MongoDB and Redis. It explains errors where they happen, holds your edits until you save them, and keeps production read-only until you say so.

Version 0.1.0 · macOS 26 Tahoe or later · Apple silicon and Intel