Automattic/mongoose · error · TypeError

batchSize must be an integer

Error message

batchSize must be an integer

What it means

eachAsync requires batchSize to be an integer. Fractional values - commonly produced by arithmetic like total / 2 without rounding - throw TypeError('batchSize must be an integer') before any documents are fetched.

Source

Thrown at lib/helpers/cursor/eachAsync.js:52

  let aborted = false;

  return new Promise((resolve, reject) => {
    if (signal != null) {
      if (signal.aborted) {
        return resolve(null);
      }

      signal.addEventListener('abort', () => {
        aborted = true;
        return resolve(null);
      }, { once: true });
    }

    if (batchSize != null) {
      if (typeof batchSize !== 'number') {
        throw new TypeError('batchSize must be a number');
      } else if (!Number.isInteger(batchSize)) {
        throw new TypeError('batchSize must be an integer');
      } else if (batchSize < 1) {
        throw new TypeError('batchSize must be at least 1');
      }
    }

    iterate((err, res) => {
      if (err != null) {
        return reject(err);
      }
      resolve(res);
    });
  });

  function iterate(finalCallback) {
    let handleResultsInProgress = 0;
    let currentDocumentIndex = 0;

    let error = null;

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Round explicitly: Math.max(1, Math.floor(computed)).
  2. Clamp the rounded value to a sane range (e.g. 1..1000).
  3. Omit batchSize when the computed value is not a positive integer.

Example fix

// before
const batchSize = docs.length / workers; // 2.5
await cursor.eachAsync(fn, { batchSize }); // TypeError

// after
const batchSize = Math.max(1, Math.floor(docs.length / workers));
await cursor.eachAsync(fn, { batchSize });
Defensive patterns

Strategy: validation

Validate before calling

function toBatchSize(v) {
  if (v == null) return undefined;
  const n = typeof v === 'number' ? v : Number(v);
  if (!Number.isInteger(n) || n < 1) {
    throw new TypeError(`invalid batchSize: ${v}`);
  }
  return n;
}
// computed sizes are validated (throws) instead of leaking fractions into eachAsync
const batchSize = toBatchSize(docs.length / workers);

Type guard

function isValidBatchSize(v) {
  return typeof v === 'number' && Number.isInteger(v) && v >= 1;
}

Prevention

When it happens

Trigger: cursor.eachAsync(fn, { batchSize: items.length / 2 }) yielding 2.5; dynamically computed adaptive batch sizes from averages or ratios; parseFloat('10.5') passed through.

Common situations: Dynamically sized batches in migration or backfill scripts; float math leaking into cursor options; unit conversions producing fractional values.

Related errors


AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21). Data as JSON: /api/errors/7da93a50066e7637. Report an issue: GitHub.