mongodb/node-mongodb-native · error · MongoUnexpectedServerResponseError
Server ended moreToCome unexpectedly
Error message
Server ended moreToCome unexpectedly
What it means
Thrown at the end of exhaustCommand's loop (src/cmap/connection.ts:676) when an exhaust cursor (getMore with awaitData/streaming) unexpectedly signaled the end of the stream - the server set moreToCome=false (or closed the socket) without the client requesting closure. Exhaust cursors are expected to keep sending; an unsolicited stop is treated as a protocol violation.
Source
Thrown at src/cmap/connection.ts:676
return document;
}
throw new MongoUnexpectedServerResponseError('Unable to get response from server');
}
public exhaustCommand(
ns: MongoDBNamespace,
command: Document,
options: CommandOptions,
replyListener: Callback
) {
const exhaustLoop = async () => {
this.throwIfAborted();
for await (const reply of this.sendCommand(ns, command, options)) {
replyListener(undefined, reply);
this.throwIfAborted();
}
throw new MongoUnexpectedServerResponseError('Server ended moreToCome unexpectedly');
};
exhaustLoop().then(undefined, replyListener);
}
private throwIfAborted() {
if (this.error) throw this.error;
}
/**
* @internal
*
* Writes an OP_MSG or OP_QUERY request to the socket, optionally compressing the command. This method
* waits until the socket's buffer has emptied (the Nodejs socket `drain` event has fired).
*/
private async writeCommand(
command: WriteProtocolMessageType,
options: {View on GitHub (pinned to 3366c21a63)
Solutions
- Wrap change-stream / exhaust consumers in a reconnect loop that re-issues the request on error.
- Ensure replica set high availability so failovers are handled by SDAM and the cursor resumes.
- For change streams, use tryNext() / hasNext() with error handling that restarts the stream at the last resumeToken.
- Check server stability - frequent exhaust termination often indicates an unstable deployment.
Example fix
// before
const cs = coll.watch();
for await (const c of cs) handle(c); // throws on failover, dies
// after
async function run() {
let cs = coll.watch();
while (true) {
try { for await (const c of cs) handle(c); }
catch (e) { cs = coll.watch([], { resumeAfter: lastToken }); }
}
} Defensive patterns
Strategy: retry
Type guard
import { MongoUnexpectedServerResponseError } from 'mongodb';
function isExhaustTerminated(e: unknown): e is MongoUnexpectedServerResponseError {
return e instanceof MongoUnexpectedServerResponseError &&
/moreToCome/.test(e.message);
} Try / catch
import { MongoUnexpectedServerResponseError } from 'mongodb';
async function watchForever(coll: any, onChange: (c: any) => void) {
let resumeToken: any;
while (true) {
const stream = coll.watch([], resumeToken ? { resumeAfter: resumeToken } : {});
try {
for await (const c of stream) { resumeToken = c._id; onChange(c); }
} catch (e) {
if (e instanceof MongoUnexpectedServerResponseError) continue;
throw e;
} finally { await stream.close().catch(() => {}); }
}
} Prevention
- Always wrap change-stream consumers in a reconnect loop with resumeAfter.
- Persist resume tokens externally so restarts resume without data loss.
- Use a replica set or sharded cluster for stable change streams.
When it happens
Trigger: Using an exhaust cursor (change streams in some configurations, or explicit exhaust:true) where the server stopped sending responses. Triggered inside the exhaustLoop closure after the for-await ends normally (src/cmap/connection.ts:670-677).
Common situations: Server failover during an exhaust stream (primary step-down); server restart; network interruption that closed the socket cleanly; change stream against a deployment that invalidated the cursor; explicit cursor.close() racing with the exhaust loop.
Related errors
- Parent provided to ChangeStream constructor must be an insta
- Unable to get response from server
- Cursor document did not contain a batch
- Cursor must be constructed with MongoClient
- Cannot specify maxAwaitTimeMS >= timeoutMS for a tailable aw
AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04).
Data as JSON: /data/errors/3a7afb3e88866265.json.
Report an issue: GitHub.