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

  1. Pass the session only as an option, never as a document field: `insertOne(doc, { session })`.
  2. Audit spreads: build the document explicitly and strip session-like references before insert.
  3. 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

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


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)