Fix: MongoDB "not primary" Write Error (Replica Set)
Quick Answer
How to fix MongoDB 'not primary' errors when writing to a replica set — read preference misconfiguration, connecting to a secondary, replica set elections, and write concern settings.
The Error
A write operation to MongoDB fails with:
MongoServerError: not primaryOr in older MongoDB versions:
MongoError: not masterOr after a failover:
MongoServerError: not primary and secondaryOk=false
MongoNotPrimaryError: Command find requires authenticationOr reads that worked suddenly fail:
MongoServerError: not primary or secondary; cannot currently read from this replSetMember.STATE=RECOVERINGWhy This Happens
MongoDB replica sets consist of one primary and one or more secondaries. Only the primary accepts write operations. This error occurs when:
- Connecting to a secondary — the connection string points to a secondary node directly, or the driver selected a secondary for a write operation.
- Read preference set to
secondaryfor writes — some drivers allow you to setreadPreference, but writes always require the primary regardless of read preference. - Replica set election in progress — after a primary failure, the replica set holds an election (typically 10–30 seconds). During this window, no primary exists and all writes fail.
- Network partition — the primary is isolated from the majority of the replica set and steps down voluntarily to prevent split-brain writes.
- Member in
RECOVERINGstate — a node that is syncing, or that just rejoined after being down, enters theRECOVERINGstate and cannot serve reads or writes. - Using a direct connection to a specific host — connecting with
directConnection=trueto a node that is currently a secondary causes immediate write failures.
Fix 1: Use a Replica Set Connection String
Never connect to a single member of a replica set using its hostname directly — always use the full replica set URI so the driver can discover the current primary:
# Wrong — connects directly to one host, fails if it's a secondary
mongodb://mongo1.example.com:27017/mydb
# Correct — lists all members; driver discovers and connects to the primary
mongodb://mongo1.example.com:27017,mongo2.example.com:27017,mongo3.example.com:27017/mydb?replicaSet=rs0With authentication:
mongodb://username:[email protected]:27017,mongo2.example.com:27017,mongo3.example.com:27017/mydb?replicaSet=rs0&authSource=adminAtlas connection string (already includes replica set info):
mongodb+srv://username:[email protected]/mydb?retryWrites=true&w=majorityThe mongodb+srv:// scheme uses DNS SRV records to discover all replica set members automatically. Always prefer this format for Atlas clusters.
Fix 2: Check and Fix Read Preference
Read preference controls which replica set member the driver sends read operations to. It does not affect where writes go — writes always go to the primary:
// Node.js (Mongoose)
const mongoose = require('mongoose');
// Wrong — setting readPreference to secondary doesn't affect writes,
// but if you're also accidentally routing writes here, it will fail
mongoose.connect(uri, {
readPreference: 'secondary', // Reads go to secondary
});
// Correct — primary for writes (default), secondaryPreferred for reads
const client = new MongoClient(uri, {
readPreference: 'secondaryPreferred', // Reads prefer secondary, fall back to primary
});Read preference modes:
| Mode | Description |
|---|---|
primary | All reads go to the primary (default) |
primaryPreferred | Reads go to primary, fall back to secondary if unavailable |
secondary | All reads go to a secondary |
secondaryPreferred | Reads prefer secondary, fall back to primary |
nearest | Reads go to the member with lowest network latency |
Common Mistake: Setting
readPreference: 'secondary'is fine for reads. The error occurs when usingdirectConnection=trueto a secondary host or when the connection string targets a secondary directly.
Python (PyMongo):
from pymongo import MongoClient, ReadPreference
# Connect with replica set — driver auto-discovers primary
client = MongoClient(
"mongodb://mongo1:27017,mongo2:27017,mongo3:27017/",
replicaSet="rs0",
readPreference=ReadPreference.SECONDARY_PREFERRED
)
db = client.mydb
# Writes automatically go to primary
db.users.insert_one({"name": "Alice"}) # Goes to primary
# Reads go to secondary (or primary if no secondary available)
users = list(db.users.find())Fix 3: Handle Replica Set Elections Gracefully
When the primary steps down (due to failure, maintenance, or rs.stepDown()), an election occurs. During the election (typically 10–30 seconds), writes fail with not primary. Configure your driver to retry writes automatically:
// Node.js — enable retryable writes (default in modern drivers)
const client = new MongoClient(uri, {
retryWrites: true, // Automatically retry eligible write operations
retryReads: true, // Automatically retry eligible read operations
});# Python — retryable writes (enabled by default in PyMongo 3.9+)
client = MongoClient(
uri,
retryWrites=True,
retryReads=True,
)Retryable writes cover:
insertOne,updateOne,replaceOne,deleteOne- Bulk write operations with
ordered: truethat fail on the first operation findOneAndUpdate,findOneAndReplace,findOneAndDelete
Not covered by retryable writes:
insertManywithordered: falseupdateMany,deleteMany- Any multi-document operation
For operations not covered, add explicit retry logic:
async function writeWithRetry(collection, doc, maxAttempts = 3) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await collection.insertOne(doc);
} catch (err) {
if (
attempt < maxAttempts &&
(err.code === 10107 || // NotPrimary
err.message.includes('not primary') ||
err.message.includes('not master'))
) {
console.warn(`Write attempt ${attempt} failed (not primary) — retrying in ${attempt * 1000}ms`);
await new Promise(resolve => setTimeout(resolve, attempt * 1000));
continue;
}
throw err;
}
}
}Fix 4: Check Replica Set Status
After a failover or if the error persists, check the replica set health:
// Connect to mongosh and check replica set status
rs.status(){
"set" : "rs0",
"members" : [
{
"name" : "mongo1:27017",
"health" : 1,
"state" : 1, // 1 = PRIMARY
"stateStr" : "PRIMARY",
...
},
{
"name" : "mongo2:27017",
"health" : 1,
"state" : 2, // 2 = SECONDARY
"stateStr" : "SECONDARY",
...
},
{
"name" : "mongo3:27017",
"health" : 0, // 0 = unhealthy
"state" : 8, // 8 = DOWN
"stateStr" : "DOWN",
...
}
]
}State values to look for:
| State | Meaning |
|---|---|
| 1 (PRIMARY) | Accepts reads and writes |
| 2 (SECONDARY) | Accepts reads (with appropriate read preference) |
| 5 (STARTUP2) | Initial sync in progress |
| 6 (UNKNOWN) | Cannot communicate with this member |
| 8 (DOWN) | Member is unreachable |
| 9 (ROLLBACK) | Rolling back operations after rejoining |
| 10 (REMOVED) | Member removed from the replica set |
If no primary exists:
// Force an election
rs.reconfig(rs.conf(), { force: true })
// Or step down the current primary (from the primary) to trigger election
rs.stepDown(60) // Steps down for 60 seconds
// Check election status
rs.isMaster() // Deprecated but still works
rs.hello() // Modern equivalentFix 5: Fix directConnection Issues
The directConnection=true option bypasses replica set topology discovery and connects directly to the specified host. This is useful for local development but causes not primary errors in production:
// Wrong — directConnection skips topology discovery
const client = new MongoClient('mongodb://mongo1:27017/mydb?directConnection=true');
// Correct — use replica set URI for production
const client = new MongoClient(
'mongodb://mongo1:27017,mongo2:27017,mongo3:27017/mydb?replicaSet=rs0'
);When directConnection=true is valid:
- Connecting to a standalone MongoDB instance (not a replica set)
- Local development with a single MongoDB instance
- Specifically targeting a secondary for maintenance operations
// Reading from a specific secondary for maintenance (not writes)
const secondaryClient = new MongoClient('mongodb://mongo2:27017/mydb', {
directConnection: true,
readPreference: 'secondaryPreferred',
});
// Never use this client for writesFix 6: Fix Write Concern Settings
Write concern controls how many replica set members must acknowledge a write before the driver considers it successful. An overly strict write concern on a degraded replica set causes writes to fail or time out:
// w: 'majority' — write must be acknowledged by the majority of members
// This fails if the majority is unreachable (e.g., 2 of 3 members are down)
const result = await collection.insertOne(doc, { writeConcern: { w: 'majority', wtimeout: 5000 } });
// w: 1 — only the primary needs to acknowledge (less safe but more available)
const result = await collection.insertOne(doc, { writeConcern: { w: 1 } });Connection-level write concern:
const client = new MongoClient(uri, {
writeConcern: {
w: 'majority',
wtimeoutMS: 5000, // Fail if majority doesn't respond in 5 seconds
journal: true, // Wait for journal flush
},
});Real-world scenario: A 3-member replica set loses 2 members. With
w: 'majority', writes fail because 2 members are needed to acknowledge. Drop tow: 1temporarily to keep writes flowing — but understand you risk data loss if the remaining primary crashes before replication occurs.
Still Not Working?
Verify the replica set name matches your connection string:
// In mongosh — check the replica set name
rs.conf().set // e.g., "rs0"
// Connection string must include replicaSet=rs0
mongodb://host1:27017,host2:27017/mydb?replicaSet=rs0Check network connectivity between application and all replica set members:
# From the application server, test each member
nc -zv mongo1.example.com 27017
nc -zv mongo2.example.com 27017
nc -zv mongo3.example.com 27017
# If any fail, the driver may not be able to discover the primaryCheck if the member is in RECOVERING state — a recovering member can’t accept reads or writes. Wait for it to finish syncing (check rs.status() and look at the optime lag).
Force a new initial sync for a stuck member:
# Stop mongod on the problematic member
sudo systemctl stop mongod
# Delete the data directory (this triggers a full resync)
sudo rm -rf /var/lib/mongodb/*
# Restart — mongod will sync from another member
sudo systemctl start mongodFor related MongoDB issues, see Fix: MongoDB Connect ECONNREFUSED and Fix: MongoDB Duplicate Key Error.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: MongoDB E11000 duplicate key error collection
How to fix the MongoDB E11000 duplicate key error by identifying duplicate fields, fixing index conflicts, using upserts, handling null values, and resolving race conditions.
Fix: AWS ECS Task Failed to Start
How to fix ECS tasks that fail to start — port binding errors, missing IAM permissions, Secrets Manager access, essential container exit codes, and health check failures.
Fix: Docker Multi-Stage Build COPY --from Failed
How to fix Docker multi-stage build errors — COPY --from stage not found, wrong stage name, artifacts not at expected path, and BuildKit caching issues.
Fix: Express req.body Is undefined
How to fix req.body being undefined in Express — missing body-parser middleware, wrong Content-Type header, middleware order issues, and multipart form data handling.