MongoDB not primary and secondaryOk=false: reading from a secondary and how to fix it
Your application logs show NotPrimaryNoSecondaryOk (code 13435) with the message "not master and slaveOk=false". Metrics show read failures against a specific host. The mongod process is running, replica set heartbeats are clean, and replication lag looks normal. The cluster is not down. The error is a routing decision: a client sent a read to a replica set member that is not the primary, without declaring that reading from a non-primary is acceptable.
By default, only the primary serves reads. Secondaries replicate the oplog asynchronously and can serve reads only when the client explicitly requests a read preference that permits them. When a driver, shell, or application sends a read to a secondary without that permission, the secondary returns this error. The server is protecting data consistency. The problem is almost always on the client side.
This error spikes in three common situations: immediately after a primary failover, when stale driver topology still points to the old primary or a secondary; when an application is misconfigured with a read preference that routes to secondaries without handling fallback correctly; and when an operator runs ad-hoc queries against a secondary without enabling secondary reads.
What this means
A MongoDB replica set maintains a strict single-primary topology. Writes go to the primary. Secondaries tail the oplog in local.oplog.rs. Because secondaries may lag, clients must explicitly opt in to secondary reads via a read preference.
This error is classified as a retryable read error in the MongoDB driver specification. When retryable reads are enabled, the driver may automatically retry the operation against a different member that satisfies the read preference. If retryable reads are disabled, or if no member satisfies the current read preference, the error surfaces to the application as a hard failure. The secondary itself is almost always healthy. It is correctly refusing to serve a read it is not configured to serve. Treat this as a client routing problem, not a server outage.
Common causes
| Cause | What it looks like | First thing to check |
|---|---|---|
| Stale topology after failover | Error spike begins exactly when a new primary is elected; may resolve after driver refresh | rs.status() member states and election timestamps in logs |
| Driver read preference misconfiguration | Reads consistently fail against secondaries while working on the primary | Connection string for readPreference, directConnection, and driver options |
Direct shell access to a secondary without secondaryOk() | Ad-hoc queries fail on one node but succeed on another | Whether the shell session targeted a secondary and if rs.secondaryOk() was run |
| Member in RECOVERING state | Reads fail against a node that is catching up after restart or sync | rs.status() stateStr for the target member |
Single-node replica set with secondary preference | Application requires secondary reads but no secondary exists | Replica set member count versus read preference mode |
Quick checks
# Check current member states across the replica set
mongosh --quiet --eval 'rs.status().members.forEach(function(m){print(m.name + " -> " + m.stateStr)})'
# Check for recent elections that could invalidate cached topology
grep -iE "election|stepping down|not electable|vote" /var/log/mongodb/mongod.log | tail -20
// Check if any member is in RECOVERING or another non-readable state
rs.status().members.forEach(function(m) {
if (m.stateStr !== 'PRIMARY' && m.stateStr !== 'SECONDARY') {
print(m.name + ': ' + m.stateStr);
}
})
// Check incoming connections and churn
var c = db.serverStatus().connections;
print('Current: ' + c.current + ' Available: ' + c.available + ' Total created: ' + c.totalCreated);
How to diagnose it
- Identify the target node. From the application error or connection logs, determine the
host:portof the MongoDB node that returned the error. If the driver does not log the specific server, check the connection string for candidate hosts. - Verify the node’s current role. Run
rs.status()from a healthy member. CheckstateStrfor the target host. If it isSECONDARY, the client routed a read to a secondary without permission. If it isPRIMARY, the client is using stale topology or a direct connection. - Check for recent elections. Search the MongoDB logs on the target node for
"Starting an election"or"Stepping down". Failover events invalidate the old primary. Drivers should refresh topology within seconds, but some configurations or network partitions delay this. If the target node was previously primary, the client is definitely using stale topology. - Inspect the application connection string. Look for
readPreference=secondary,readPreference=secondaryPreferred,directConnection=true, or no read preference at all. If the string usessecondary, confirm the replica set has at least one healthySECONDARY. If it usesprimaryor omits the option, confirm the driver is not overriding it elsewhere. - Check for RECOVERING members. A node in
RECOVERINGcannot serve reads even withsecondaryPreferred. Verify all expected secondaries showstateStr: "SECONDARY"andhealth: 1. - Validate driver retry behavior. Modern drivers enable retryable reads by default. If the application uses an older driver or explicitly disables retries, transient
NotPrimaryNoSecondaryOkerrors during failover will not self-heal.
Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
| Replica set member state | Identifies whether the target node is PRIMARY, SECONDARY, or RECOVERING | Node is SECONDARY or RECOVERING when reads fail |
| Election events | Elections change the primary and invalidate cached topology | Log entries for elections coincide with error spikes |
| Replication lag | High lag can cause a secondary to enter RECOVERING or fall off the oplog | Lag >10 seconds sustained on secondaries |
| Connection utilization | Reconnection storms after failover delay recovery and amplify errors | totalCreated delta spikes after topology changes |
Assertion counts (user) | Client-side errors increment user assertions; spikes may reveal misconfiguration | Sustained increase in user assertion rate |
Fixes
Stale topology after failover
Do not restart mongod. The server is healthy. The driver’s cached view of the replica set topology is stale. Ensure the application uses a modern driver with retryable reads enabled. If errors persist after the cluster has elected a stable primary, restart the application process to force the driver to rebuild its connection pool and topology map. This is disruptive, so use it only after the cluster is stable.
Check the driver’s serverSelectionTimeoutMS and heartbeatFrequencyMS. A heartbeat frequency that is too low delays detection of stepdowns. A serverSelectionTimeoutMS that is too short may cause the driver to give up before finding the new primary. The default serverSelectionTimeoutMS is typically 30 seconds; do not lower it unless you understand the tradeoff.
Misconfigured read preference
If the application intends to read from secondaries, update the connection string to use a read preference that matches the topology reality. Use readPreference=secondaryPreferred instead of secondary unless the application explicitly requires failure when no secondary is available. primaryPreferred is safer for workloads that prefer the primary but can tolerate occasional secondary reads during failover. nearest routes to the member with the lowest latency based on local measurements.
Multi-document transactions must use the primary read preference. All operations in a transaction must route to the same member. Attempting to use secondary inside a transaction will fail.
For ad-hoc queries in mongosh against a secondary, run rs.secondaryOk() before querying. Do not use rs.slaveOk(), which is deprecated and may be removed.
Member in RECOVERING state
Wait for the member to finish initial sync or oplog catch-up. Check rs.status() for stateStr. If the member remains in RECOVERING for more than a few minutes, investigate storage latency, network throughput between members, or oplog window size. Reads against a recovering node will fail regardless of read preference. Do not attempt to force reads against it.
Single-node replica sets
A replica set with one member has no secondary. Any readPreference=secondary configuration will fail immediately because no eligible secondary exists. Change the application to use primary or primaryPreferred. If you need secondary reads for load distribution, add members to the replica set.
Prevention
- Use
secondaryPreferredrather thansecondaryunless your application explicitly requires failure when no secondary exists.secondaryPreferredfalls back to the primary when secondaries are unavailable, which prevents outages during maintenance or failover. - Enable retryable reads in the driver to absorb transient routing errors during failover without application-level retries.
- Monitor election events and alert on any unexpected election outside maintenance windows. A single unexpected election is a stability concern.
- Use the replica set connection string format with multiple seed hosts so drivers can discover the current primary without relying on a single endpoint. Single-host connections are more fragile during failovers. Avoid
directConnection=truein replica set applications unless you are intentionally connecting to a single node. - Keep MongoDB drivers current. Older drivers have slower topology refresh and weaker handling of stepdown events.
- If your application cannot tolerate stale data, avoid routing reads to delayed secondaries. Use
maxStalenessSecondsto exclude secondaries that are too far behind.
How Netdata helps
- Replica set member state is collected continuously, so you see a node transition from
PRIMARYtoSECONDARYat the exact moment the error begins. - Election events can be surfaced through log monitoring or metric correlation, linking error spikes directly to failovers.
- Replication lag per secondary shows whether secondaries are healthy enough to serve reads when
secondaryPreferredis in use. - Connection utilization and
totalCreatedtrends reveal reconnection storms that delay topology recovery. - Assertion counts (
user,regular) help distinguish client misconfiguration from server-side degradation.
Related guides
- How MongoDB actually works in production: a mental model for operators
- MongoDB pages evicted by application threads: when eviction becomes user latency
- MongoDB WiredTiger cache dirty ratio high: the leading indicator nobody watches
- MongoDB WiredTiger cache pressure cascade: eviction stalls and latency spikes
- MongoDB cache too small: sizing the WiredTiger cache for your working set
- MongoDB checkpoint duration climbing: diagnosing slow WiredTiger checkpoints
- MongoDB checkpoint stall write freeze: when all writes stop with no error
- MongoDB journal sync latency high: the storage signal that warns 60 seconds early
- MongoDB monitoring checklist: the signals every production cluster needs
- MongoDB monitoring maturity model: from survival to expert
- MongoDB noTimeout cursors causing cache pressure: pinned snapshots and silent eviction stalls
- MongoDB oplog window collapse: secondaries falling off and forced full resync







