mongodb/node-mongodb-native · error · MongoInvalidArgumentError
An operation cannot be given a timeoutMS setting when inside
Error message
An operation cannot be given a timeoutMS setting when inside a withTransaction call that has a timeoutMS setting
What it means
Thrown by resolveOptions() when an operation is executed inside a 'convenient transaction' (a withTransaction() call that carries its own timeoutMS) AND the individual operation is also given a timeoutMS. The convenient-transaction API owns the timeout budget for the whole transaction, so a nested per-operation timeoutMS is contradictory and forbidden. It surfaces as a MongoInvalidArgumentError to prevent ambiguous/competing timeout semantics.
Source
Thrown at src/utils.ts:551
wtimeout: undefined,
wtimeoutMS: undefined
}
});
}
result.writeConcern = writeConcern;
}
}
result.timeoutMS = timeoutMS;
const readPreference = ReadPreference.fromOptions(options) ?? parent?.readPreference;
if (readPreference) {
result.readPreference = readPreference;
}
const isConvenientTransaction = session?.explicit && session?.timeoutContext != null;
if (isConvenientTransaction && options?.timeoutMS != null) {
throw new MongoInvalidArgumentError(
'An operation cannot be given a timeoutMS setting when inside a withTransaction call that has a timeoutMS setting'
);
}
return result;
}
export function isSuperset(set: Set<any> | any[], subset: Set<any> | any[]): boolean {
set = Array.isArray(set) ? new Set(set) : set;
subset = Array.isArray(subset) ? new Set(subset) : subset;
for (const elem of subset) {
if (!set.has(elem)) {
return false;
}
}
return true;
}
View on GitHub (pinned to 3366c21a63)
Solutions
- Remove the per-operation timeoutMS inside the withTransaction callback; let the transaction's timeoutMS govern.
- If a specific operation needs a shorter bound, enforce it with your own Promise.race/AbortController instead of timeoutMS.
- Audit shared option builders so timeoutMS is not spread into operations that run under withTransaction.
Example fix
// before
await session.withTransaction(
async (session) => {
await coll.insertOne({ a: 1 }, { session, timeoutMS: 1000 }); // throws
},
{ timeoutMS: 5000 }
);
// after
await session.withTransaction(
async (session) => {
await coll.insertOne({ a: 1 }, { session }); // no nested timeoutMS
},
{ timeoutMS: 5000 }
); Defensive patterns
Strategy: validation
Validate before calling
function stripTimeoutInsideTx<T extends { timeoutMS?: number; session?: ClientSession }>(opts: T | undefined): T {
if (opts?.session?.inTransaction() && opts.timeoutMS != null) {
const { timeoutMS, ...rest } = opts;
return rest as T;
}
return opts ?? ({} as T);
} Type guard
function isInsideConvenientTransaction(session?: ClientSession): boolean {
return !!session?.explicit && (session as any).timeoutContext != null;
} Prevention
- Never pass timeoutMS to operations running inside withTransaction.
- Keep transactional and non-transactional option objects separate.
- Let the transaction-level timeoutMS own the budget.
When it happens
Trigger: Inside `session.withTransaction(async (session) => { ... }, { timeoutMS: 5000 })`, calling any operation with an explicit `{ timeoutMS: N }` option, e.g. `coll.findOne({}, { session, timeoutMS: 1000 })`.
Common situations: Copying an operation helper that hardcodes timeoutMS into a withTransaction callback; layering the new timeoutMS API (v6+) onto existing transaction code without removing per-call timeouts; shared option objects spread into both transactional and non-transactional calls.
Related errors
- Argument for maxAwaitTimeMS must be a number
- Argument for maxTimeMS must be a number
- Update document requires atomic operators
- Replacement document must not use atomic operators
- Bulk find operation must specify a selector
AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04).
Data as JSON: /data/errors/3f6aeabb02c8a9e0.json.
Report an issue: GitHub.