Automattic/mongoose · error · TypeError

batchSize must be at least 1

Error message

batchSize must be at least 1

What it means

eachAsync rejects batchSize values below 1: 0 and negative numbers throw TypeError('batchSize must be at least 1'). batchSize of 0 usually means the developer intended no batching or driver default, but the server requires at least one document per batch, so Mongoose validates the lower bound eagerly.

Solutions

  1. Treat 0/negative as unset: const opts = n > 0 ? { batchSize: n } : {};.
  2. Or clamp: batchSize: Math.max(1, n).
  3. Fix the source producing non-positive values (env defaults, empty-length divisions).

Example fix

// before
const batchSize = Number(process.env.BATCH_SIZE ?? 0);
await cursor.eachAsync(fn, { batchSize }); // 0 -> TypeError

// after
const raw = Number(process.env.BATCH_SIZE ?? NaN);
const batchSize = Number.isInteger(raw) && raw >= 1 ? raw : undefined;
await cursor.eachAsync(fn, { batchSize }); // undefined = driver default
Defensive patterns

Strategy: validation

Validate before calling

function toBatchSize(v) {
  const n = v == null ? NaN : Number(v);
  return Number.isInteger(n) && n >= 1 ? n : undefined; // undefined = driver default
}
await cursor.eachAsync(fn, { batchSize: toBatchSize(process.env.BATCH_SIZE) });

Type guard

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

Prevention

When it happens

Trigger: cursor.eachAsync(fn, { batchSize: 0 }) intending unlimited; negative values from misconfigured env vars or arithmetic underflow; -1 sentinels copied from a different API's semantics.

Common situations: Env var defaulting to 0 when unset; computed batch sizes that reach 0 for empty inputs; conventions from other libraries where 0 means default.

Related errors


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

Appendix: source

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

  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;
    for (let i = 0; i < parallel; ++i) {
      enqueue(createFetch());

View on GitHub (pinned to 49cdab0136)