mongodb/node-mongodb-native · error · MongoInvalidArgumentError

Function provided to `withTransaction` must return a Promise

Error message

Function provided to `withTransaction` must return a Promise

What it means

`withTransaction` requires its callback to return a thenable so the driver can await it, track errors, and retry. After invoking the callback the driver runs `isPromiseLike(promise)`; if it is not a Promise it throws MongoInvalidArgumentError (sessions.ts:818). This is enforced because synchronous callbacks cannot propagate transactional errors correctly.

Solutions

  1. Mark the callback `async`: `session.withTransaction(async (s) => { ... })`.
  2. Ensure every async operation inside is awaited (or returned) so the callback returns a Promise.
  3. If your helper builds the callback dynamically, wrap a sync function with `async (s) => { fn(s); }`.

Example fix

// before
await session.withTransaction((s) => {
  collection.insertOne(doc, { session: s });
});

// after
await session.withTransaction(async (s) => {
  await collection.insertOne(doc, { session: s });
});
Defensive patterns

Strategy: type-guard

Validate before calling

function isPromise(v) {
  return v != null && typeof v.then === 'function';
}
async function runWithTxn(session, fn) {
  return session.withTransaction(async (s) => fn(s));
}

Type guard

function returnsPromise(fn) {
  // best-effaint static check: callbacks passed to withTransaction should be async
  return fn.constructor?.name === 'AsyncFunction';
}

Prevention

When it happens

Trigger: `session.withTransaction((s) => { collection.insertOne(doc, { session: s }); })` — the arrow function returns undefined (no `async`, no `return`). Also a callback that returns a plain value instead of a Promise.

Common situations: Forgetting `async` on the callback; calling an async function without `return await`/`return`; callback written synchronously by mistake.

Related errors


AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11). Data as JSON: /api/errors/32fa8fe223d5e625. Report an issue: GitHub.

Appendix: source

Thrown at src/sessions.ts:819

          }

          await setTimeout(backoffMS);
        }

        // 3. Invoke startTransaction on the session and increment transactionAttempt. If TransactionOptions were
        // specified in the call to withTransaction, those MUST be used for startTransaction. Note that
        // ClientSession.defaultTransactionOptions will be used in the absence of any explicit TransactionOptions.
        // 4. If startTransaction reported an error, propagate that error to the caller of withTransaction as is and
        // return immediately.
        this.startTransaction(options);

        try {
          // 5. Invoke the callback. Drivers MUST ensure that the ClientSession can be accessed within the callback
          // (e.g. pass ClientSession as the first parameter, rely on lexical scoping). Drivers MAY pass additional
          // parameters as needed (e.g. user data solicited by withTransaction).
          const promise = fn(this);
          if (!isPromiseLike(promise)) {
            throw new MongoInvalidArgumentError(
              'Function provided to `withTransaction` must return a Promise'
            );
          }

          // 6. Control returns to withTransaction. Determine the current state of the ClientSession and whether the
          // callback reported an error (e.g. thrown exception, error output parameter).
          result = await promise;

          // 8. If the ClientSession is in the "no transaction", "transaction aborted", or "transaction committed"
          // state, assume the callback intentionally aborted or committed the transaction and return immediately.
          if (
            this.transaction.state === TxnState.NO_TRANSACTION ||
            this.transaction.state === TxnState.TRANSACTION_COMMITTED ||
            this.transaction.state === TxnState.TRANSACTION_ABORTED
          ) {
            return result;
          }
        } catch (fnError) {

View on GitHub (pinned to dce7939f86)