Automattic/mongoose · error · TypeError
batchSize must be a number
Error message
batchSize must be a number
What it means
eachAsync on a query or aggregation cursor accepts an options object whose batchSize controls how many documents are fetched per batch. The helper validates batchSize eagerly: it must be a JavaScript number. Passing '100' (a string from an env var or query param) or any non-number throws TypeError before iteration starts; the check only runs when batchSize is not null/undefined.
Source
Thrown at lib/helpers/cursor/eachAsync.js:50
const enqueue = asyncQueue();
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;View on GitHub (pinned to 49cdab0136)
Solutions
- Coerce before passing: batchSize: Number(process.env.BATCH_SIZE).
- Parse query params explicitly: const batchSize = parseInt(req.query.batchSize, 10).
- Omit batchSize entirely (pass undefined) when it is not configured - the validation skips null/undefined.
Example fix
// before
const batchSize = process.env.BATCH_SIZE; // string
await Model.find().cursor().eachAsync(fn, { batchSize }); // TypeError
// after
const batchSize = process.env.BATCH_SIZE != null ? Number(process.env.BATCH_SIZE) : undefined;
await Model.find().cursor().eachAsync(fn, { batchSize }); Defensive patterns
Strategy: validation
Validate before calling
function toBatchSize(v) {
if (v == null) return undefined;
const n = Number(v);
if (!Number.isInteger(n) || n < 1) {
throw new TypeError(`invalid batchSize: ${v}`);
}
return n;
}
await cursor.eachAsync(fn, { batchSize: toBatchSize(rawValue) }); Type guard
function isValidBatchSize(v) {
return typeof v === 'number' && Number.isInteger(v) && v >= 1;
} Prevention
- Convert env vars and query-string inputs to numbers at the boundary (Number(), zod, joi).
- Validate cursor option objects once in a shared helper instead of at every call site.
- Treat null/undefined as unset rather than 0.
When it happens
Trigger: cursor.eachAsync(fn, { batchSize: '100' }); batchSize read from process.env.BATCH_SIZE or a URL query parameter without conversion; a shared utility forwarding caller-supplied strings into eachAsync.
Common situations: Config-driven batch sizes in ETL/backfill scripts; API handlers forwarding query-string options directly into cursor options; defaults that accidentally set batchSize to a non-number.
Related errors
- batchSize must be an integer
- batchSize must be at least 1
- Arguments must be aggregate pipeline operators
- Invalid addFields() argument. Must be an object
- Invalid project() argument. Must be string or object
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/0bd12bab595f93bd.
Report an issue: GitHub.