Automattic/mongoose · error · MongooseError
Cannot set a document's session to a session that has ended.
Error message
Cannot set a document's session to a session that has ended. Make sure you haven't called `endSession()` on the session you are passing to `$session()`.
What it means
$session(session) attaches a ClientSession to the document (and propagates it to subdocuments) so subsequent saves run inside it. Mongoose rejects sessions whose `hasEnded` flag is set: after endSession() the session can carry no further operations, so attaching one would guarantee driver failures on every later command.
Source
Thrown at lib/document.js:985
*
* @param {ClientSession} [session] overwrite the current session
* @return {ClientSession}
* @method $session
* @api public
* @memberOf Document
*/
Document.prototype.$session = function $session(session) {
if (arguments.length === 0) {
if (this.$__.session?.hasEnded) {
this.$__.session = null;
return null;
}
return this.$__.session;
}
if (session?.hasEnded) {
throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' +
'called `endSession()` on the session you are passing to `$session()`.');
}
if (session == null && this.$__.session == null) {
return;
}
this.$__.session = session;
if (!this.$isSubdocument) {
const subdocs = this.$getAllSubdocs();
for (const child of subdocs) {
child.$session(session);
}
}
return session;
};View on GitHub (pinned to 49cdab0136)
Solutions
- Call `doc.$session(null)` once the session's work is done, and start a fresh session with conn.startSession() for later operations
- Move endSession() to after the last operation that uses the session (end of transaction/request)
- Guard every attach: `if (session != null && !session.hasEnded) doc.$session(session)`
Example fix
// before await session.endSession(); doc.$session(session); // throws: session already ended // after await session.endSession(); doc.$session(null); const s2 = await conn.startSession(); doc.$session(s2);
Defensive patterns
Strategy: validation
Validate before calling
function attachSession(doc, session) {
if (session != null && session.hasEnded) {
throw new Error('Refusing to attach an ended session; start a new one');
}
doc.$session(session ?? null);
} Type guard
/** @param {import('mongodb').ClientSession | null | undefined} s */
const isUsableSession = (s) => s == null || s.hasEnded !== true; Try / catch
try {
doc.$session(s);
} catch (err) {
if (/session that has ended/.test(err.message)) {
doc.$session(null);
s = await conn.startSession();
doc.$session(s);
} else { throw err; }
} Prevention
- Treat a session as dead as soon as withTransaction()/withSession() callbacks return
- Never pool or cache ClientSession objects across requests
- End sessions only in the scope that created them, after the final await
When it happens
Trigger: `const s = await conn.startSession(); await s.endSession(); doc.$session(s)`; storing a session on a document past the end of a withTransaction()/withSession() block (both end the session when their callback resolves); sessions ended by connection.close().
Common situations: Session lifecycle owned by a request wrapper while documents outlive the request; caching or pooling ClientSession objects; ending the session in a finally block before the last save() has run.
Related errors
- Invalid arguments
- Must call `setClient()` with an instance of MongoClient
- Cannot call `setClient()` with a MongoClient that you have n
- Cannot call `create()` with a session and multiple documents
- Arguments must be aggregate pipeline operators
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/95f9bbe01f85ec0f.
Report an issue: GitHub.