mongodb/node-mongodb-native · error · MongoTransactionError

Read preference in a transaction must be primary, not: ${rea

Error message

Read preference in a transaction must be primary, not: ${readPreference.mode}

What it means

executeOperation (src/operations/execute_operation.ts:110) throws a MongoTransactionError when an operation is executed inside an active transaction but has a non-primary read preference. The MongoDB transaction specification requires all operations within a transaction to target the primary, so the driver rejects any secondary/nearest/etc. read preference up front.

Source

Thrown at src/operations/execute_operation.ts:110

  ) {
    throw new MongoCompatibilityError('Snapshot reads require MongoDB 5.0 or later');
  } else if (session.client !== client) {
    throw new MongoInvalidArgumentError('ClientSession must be from the same MongoClient');
  }

  operation.session ??= session;

  const readPreference = operation.readPreference ?? ReadPreference.primary;
  const inTransaction = !!session?.inTransaction();

  const hasReadAspect = operation.hasAspect(Aspect.READ_OPERATION);

  if (
    inTransaction &&
    !readPreference.equals(ReadPreference.primary) &&
    (hasReadAspect || operation.commandName === 'runCommand')
  ) {
    throw new MongoTransactionError(
      `Read preference in a transaction must be primary, not: ${readPreference.mode}`
    );
  }

  if (session?.isPinned && session.transaction.isCommitted && !operation.bypassPinningCheck) {
    session.unpin();
  }

  timeoutContext ??= TimeoutContext.create({
    session,
    serverSelectionTimeoutMS: client.s.options.serverSelectionTimeoutMS,
    waitQueueTimeoutMS: client.s.options.waitQueueTimeoutMS,
    timeoutMS: operation.options.timeoutMS
  });

  try {
    return await executeOperationWithRetries(operation, {
      topology,

View on GitHub (pinned to 3366c21a63)

Solutions

  1. Inside a transaction, do not set a non-primary read preference — the default primary is correct.
  2. If readPreference was set at the collection or db level, override it on the operation or use a separate client/collection without the preference for transactional work.
  3. Move secondary reads outside the transaction boundary.

Example fix

// before
const coll = db.collection('x', { readPreference: 'secondary' });
await session.withTransaction(async () => {
  await coll.find({}).toArray(); // throws
});

// after
const coll = db.collection('x'); // no secondary preference for transactional access
await session.withTransaction(async () => {
  await coll.find({}).toArray();
});
Defensive patterns

Strategy: validation

Validate before calling

import { ReadPreference } from 'mongodb';
function safeReadPreferenceForTransaction(
  rp: ReadPreference | undefined,
  inTransaction: boolean
): ReadPreference {
  return inTransaction ? ReadPreference.primary : (rp ?? ReadPreference.primary);
}

Try / catch

try {
  await collection.find({}, { readPreference }).toArray();
} catch (err) {
  if (err instanceof MongoTransactionError && /Read preference/.test(err.message)) {
    // re-run with primary read preference
    await collection.find({}).toArray();
  } else throw err;
}

Prevention

When it happens

Trigger: Calling collection.find(filter, { readPreference: 'secondary' }) (or setting readPreference on the collection/db/client level) while a session transaction is active, i.e. session.inTransaction() is true.

Common situations: Setting a global readPreference on the MongoClient for analytics workloads, then using the same client inside a transaction without overriding the preference. Also triggered by runCommand inside a transaction with a non-primary mode.

Related errors


AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04). Data as JSON: /data/errors/5fc283c5ed2433ee.json. Report an issue: GitHub.