{"id":"5fc283c5ed2433ee","repo":"mongodb/node-mongodb-native","slug":"read-preference-in-a-transaction-must-be-primary","errorCode":null,"errorMessage":"Read preference in a transaction must be primary, not: ${readPreference.mode}","messagePattern":"Read preference in a transaction must be primary, not: (.+?)","errorType":"exception","errorClass":"MongoTransactionError","httpStatus":null,"severity":"error","filePath":"src/operations/execute_operation.ts","lineNumber":110,"sourceCode":"  ) {\n    throw new MongoCompatibilityError('Snapshot reads require MongoDB 5.0 or later');\n  } else if (session.client !== client) {\n    throw new MongoInvalidArgumentError('ClientSession must be from the same MongoClient');\n  }\n\n  operation.session ??= session;\n\n  const readPreference = operation.readPreference ?? ReadPreference.primary;\n  const inTransaction = !!session?.inTransaction();\n\n  const hasReadAspect = operation.hasAspect(Aspect.READ_OPERATION);\n\n  if (\n    inTransaction &&\n    !readPreference.equals(ReadPreference.primary) &&\n    (hasReadAspect || operation.commandName === 'runCommand')\n  ) {\n    throw new MongoTransactionError(\n      `Read preference in a transaction must be primary, not: ${readPreference.mode}`\n    );\n  }\n\n  if (session?.isPinned && session.transaction.isCommitted && !operation.bypassPinningCheck) {\n    session.unpin();\n  }\n\n  timeoutContext ??= TimeoutContext.create({\n    session,\n    serverSelectionTimeoutMS: client.s.options.serverSelectionTimeoutMS,\n    waitQueueTimeoutMS: client.s.options.waitQueueTimeoutMS,\n    timeoutMS: operation.options.timeoutMS\n  });\n\n  try {\n    return await executeOperationWithRetries(operation, {\n      topology,","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/3366c21a6311e02f1be91da982f9b93d3cce99a0/src/operations/execute_operation.ts#L92-L128","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inside a transaction, do not set a non-primary read preference — the default primary is correct.","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.","Move secondary reads outside the transaction boundary."],"exampleFix":"// before\nconst coll = db.collection('x', { readPreference: 'secondary' });\nawait session.withTransaction(async () => {\n  await coll.find({}).toArray(); // throws\n});\n\n// after\nconst coll = db.collection('x'); // no secondary preference for transactional access\nawait session.withTransaction(async () => {\n  await coll.find({}).toArray();\n});","handlingStrategy":"validation","validationCode":"import { ReadPreference } from 'mongodb';\nfunction safeReadPreferenceForTransaction(\n  rp: ReadPreference | undefined,\n  inTransaction: boolean\n): ReadPreference {\n  return inTransaction ? ReadPreference.primary : (rp ?? ReadPreference.primary);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await collection.find({}, { readPreference }).toArray();\n} catch (err) {\n  if (err instanceof MongoTransactionError && /Read preference/.test(err.message)) {\n    // re-run with primary read preference\n    await collection.find({}).toArray();\n  } else throw err;\n}","preventionTips":["Do not set non-primary read preferences at client/db/collection level if transactions are used.","Inside withTransaction, always target primary.","Keep analytical secondary reads outside transaction boundaries."],"tags":["transaction","read-preference","spec"],"analyzedSha":"3366c21a6311e02f1be91da982f9b93d3cce99a0","analyzedAt":"2026-08-04T13:40:15.335Z","schemaVersion":2}