Fix MongoDB 'Transaction numbers are only allowed on a replica set member or mongos' Error

intermediate๐Ÿƒ MongoDB2026-03-21| MongoDB 4.0+, Node.js / Python / any driver, standalone mongod instance (not replica set)

Error Message

MongoServerError: Transaction numbers are only allowed on a replica set member or mongos
#mongodb#transaction#replica-set#standalone#session

What happened

You wired up multi-document transaction support, ran the app locally, and got slapped with this:

MongoServerError: Transaction numbers are only allowed on a replica set member or mongos

Nine times out of ten it shows up in a local dev environment. MongoDB is running as a plain standalone mongod โ€” the default when you install via a package manager or start it with no config file. Transactions need either a replica set or a sharded cluster (mongos). Standalone mode simply doesn't support them, full stop.

Confirming the root cause

Check what mode your instance is actually running before touching anything:

mongosh --eval "db.adminCommand({ isMaster: 1 })"

No "setName" field in the output? You're on a standalone node. That's your culprit.

{
  "ismaster": true,
  "maxBsonObjectSize": 16777216,
  ...
  // No "setName" field = standalone mode
}

A replica set member looks different โ€” it always carries the set name:

{
  "setName": "rs0",
  "ismaster": true,
  ...
}

Note: On MongoDB 5.0+, isMaster is deprecated. Use db.adminCommand({ hello: 1 }) instead โ€” same output structure, just the preferred command going forward.

The fix: convert standalone to a single-node replica set

You don't need three nodes for local dev. A single-node replica set unlocks full transaction support with essentially zero overhead. There are three ways to get there depending on your setup.

Option 1 โ€” Edit mongod.conf (recommended)

Find your config file โ€” typically /etc/mongod.conf on Linux or /usr/local/etc/mongod.conf on macOS with Homebrew. Add the replication block:

# /etc/mongod.conf
replication:
  replSetName: "rs0"

Restart the service:

# Linux (systemd)
sudo systemctl restart mongod

# macOS (Homebrew)
brew services restart mongodb-community

Then kick off replica set initiation โ€” this is a one-time step:

mongosh --eval "rs.initiate()"

You should see:

{ "ok": 1 }

Option 2 โ€” Command-line flag (quick smoke test)

Starting mongod manually without a config file? Pass the flag directly:

mongod --replSet rs0 --dbpath /data/db

Open a second terminal and initiate the set once:

mongosh --eval "rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: 'localhost:27017' }] })"

Option 3 โ€” Docker Compose

The tricky part with containers is that rs.initiate() has to run after MongoDB is healthy. An init container handles that cleanly:

services:
  mongo:
    image: mongo:7
    command: ["--replSet", "rs0", "--bind_ip_all"]
    ports:
      - "27017:27017"
    healthcheck:
      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
      interval: 10s
      timeout: 5s
      retries: 5

  mongo-init:
    image: mongo:7
    depends_on:
      mongo:
        condition: service_healthy
    restart: "no"
    entrypoint: >
      mongosh --host mongo:27017 --eval
      "rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: 'mongo:27017' }] })"

docker compose up -d

Verify the fix

Pull up the replica set status:

mongosh --eval "rs.status()"

Find "stateStr" : "PRIMARY" in the members array. Good. Now run a transaction directly to confirm it works end-to-end:

// Quick smoke test in mongosh
const session = db.getMongo().startSession();
session.startTransaction();
try {
  session.getDatabase('test').orders.insertOne({ item: 'test' }, { session });
  session.commitTransaction();
  print('Transaction committed OK');
} catch (e) {
  session.abortTransaction();
  print('Transaction failed:', e.message);
} finally {
  session.endSession();
}

Transaction committed OK? You're done.

Update your connection string

Once you switch to replica set mode, add the replica set name to your connection string. Skip this and some drivers won't enable transaction support even when talking to a replica set โ€” it's a frustrating gotcha.

# Before
mongodb://localhost:27017/mydb

# After
mongodb://localhost:27017/mydb?replicaSet=rs0

Node.js with Mongoose:

await mongoose.connect('mongodb://localhost:27017/mydb?replicaSet=rs0');

Python with PyMongo:

from pymongo import MongoClient
client = MongoClient('mongodb://localhost:27017/mydb?replicaSet=rs0')

What about production?

Hitting this in production means your deployment is running standalone MongoDB โ€” no automatic failover, no data redundancy. Fix that regardless of transactions. A proper 3-node replica set is the baseline for any production workload. Most managed services (Atlas, MongoDB on DigitalOcean, etc.) provision replica sets by default, so this is really a self-hosted concern. The MongoDB replica set deployment guide covers the full setup.

Lessons learned

  • MongoDB transactions have required a replica set since version 4.0 โ€” this is a hard architectural constraint, not a config toggle.
  • Running a single-node replica set in dev costs you nothing and prevents this exact surprise when code hits staging.
  • Always add ?replicaSet=rs0 to local connection strings after making this change โ€” it avoids confusing driver behavior around transaction discovery.
  • In CI with bare mongod, the Docker Compose init-container pattern is the most reliable way to handle the one-time rs.initiate() call without race conditions.

Related Error Notes