mongodb/node-mongodb-native · error · MongoRuntimeError
ClientSession cannot be serialized to BSON.
Error message
ClientSession cannot be serialized to BSON.
What it means
ClientSession defines `toBSON(): never` that always throws MongoRuntimeError (sessions.ts:675). The BSON serializer calls `toBSON()` on any object that defines it, so accidentally embedding a ClientSession in a document being inserted/updated/replaced triggers this guard. It exists precisely to catch the common mistake of passing `{ session }` as a field rather than as an option.
Solutions
- Pass the session only as an option, never as a document field: `insertOne(doc, { session })`.
- Audit spreads: build the document explicitly and strip session-like references before insert.
- If you must carry context, store a session id (string) in the document, not the session object.
Example fix
// before
await collection.insertOne({ session, name: 'x' }, { session });
// after
await collection.insertOne({ name: 'x' }, { session }); Defensive patterns
Strategy: type-guard
Validate before calling
import { ClientSession } from 'mongodb';
function stripSessions(doc) {
for (const k of Object.keys(doc)) {
if (doc[k] instanceof ClientSession) delete doc[k];
else if (doc[k] && typeof doc[k] === 'object') stripSessions(doc[k]);
}
return doc;
}
await collection.insertOne(stripSessionFields(payload), { session }); Type guard
import { ClientSession } from 'mongodb';
function hasSessionValue(v) {
if (v instanceof ClientSession) return true;
if (v && typeof v === 'object') {
return Object.values(v).some(hasSessionValue);
}
return false;
} Prevention
- Pass the session only as the second argument option, never as a document field.
- Be careful with spreads of request bodies that may carry a session reference.
- Add a unit test that asserts inserted documents never contain a ClientSession.
When it happens
Trigger: `collection.insertOne({ session, ...data }, { session })` — the session object ends up as a document field; similarly for updateOne/replaceOne/aggregate stages that embed the session by mistake. Spreads like `collection.insertOne({ ...reqBody })` where `reqBody` happens to contain a session reference.
Common situations: Mixing up the session-as-option vs. session-as-document; spreading a request body that was decorated with a session; passing a document that includes a nested entity holding a session.
Related errors
- Could not serialize operation to BSON
- Function provided to `withTransaction` must return a Promise
- input cluster time "clusterTime" property must be a valid…
- input cluster time must be an object
- input cluster time must have a valid "signature" property…
AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11).
Data as JSON: /api/errors/000ccfdf1a07412e.
Report an issue: GitHub.
Appendix: source
Thrown at src/sessions.ts:676
}
// we do not retry the retry
}
}
// The spec indicates that if the operation times out or fails with a non-retryable error, we should ignore all errors on `abortTransaction`
} finally {
this.transaction.transition(TxnState.TRANSACTION_ABORTED);
if (this.loadBalanced) {
maybeClearPinnedConnection(this, { force: false });
}
}
}
/**
* This is here to ensure that ClientSession is never serialized to BSON.
*/
toBSON(): never {
throw new MongoRuntimeError('ClientSession cannot be serialized to BSON.');
}
/**
* Starts a transaction and runs a provided function, ensuring the commitTransaction is always attempted when all operations run in the function have completed.
*
* **IMPORTANT:** This method requires the function passed in to return a Promise. That promise must be made by `await`-ing all operations in such a way that rejections are propagated to the returned promise.
*
* **IMPORTANT:** Running operations in parallel is not supported during a transaction. The use of `Promise.all`,
* `Promise.allSettled`, `Promise.race`, etc to parallelize operations inside a transaction is
* undefined behaviour.
*
* **IMPORTANT:** When running an operation inside a `withTransaction` callback, if it is not
* provided the explicit session in its options, it will not be part of the transaction and it will not respect timeoutMS.
*
*
* @remarks
* - If all operations successfully complete and the `commitTransaction` operation is successful, then the provided function will return the result of the provided function.
* - If the transaction is unable to complete or an error is thrown from within the provided function, then the provided function will throw an error.View on GitHub (pinned to dce7939f86)