{"record":{"id":"96095913694bae8a","repo":"Automattic/mongoose","slug":"batchsize-must-be-at-least-1","errorCode":null,"errorMessage":"batchSize must be at least 1","messagePattern":"batchSize must be at least 1","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"lib/helpers/cursor/eachAsync.js","lineNumber":54,"sourceCode":"  return new Promise((resolve, reject) => {\n    if (signal != null) {\n      if (signal.aborted) {\n        return resolve(null);\n      }\n\n      signal.addEventListener('abort', () => {\n        aborted = true;\n        return resolve(null);\n      }, { once: true });\n    }\n\n    if (batchSize != null) {\n      if (typeof batchSize !== 'number') {\n        throw new TypeError('batchSize must be a number');\n      } else if (!Number.isInteger(batchSize)) {\n        throw new TypeError('batchSize must be an integer');\n      } else if (batchSize < 1) {\n        throw new TypeError('batchSize must be at least 1');\n      }\n    }\n\n    iterate((err, res) => {\n      if (err != null) {\n        return reject(err);\n      }\n      resolve(res);\n    });\n  });\n\n  function iterate(finalCallback) {\n    let handleResultsInProgress = 0;\n    let currentDocumentIndex = 0;\n\n    let error = null;\n    for (let i = 0; i < parallel; ++i) {\n      enqueue(createFetch());","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/helpers/cursor/eachAsync.js#L36-L72","documentation":"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.","triggerScenarios":"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.","commonSituations":"Env var defaulting to 0 when unset; computed batch sizes that reach 0 for empty inputs; conventions from other libraries where 0 means default.","solutions":["Treat 0/negative as unset: const opts = n > 0 ? { batchSize: n } : {};.","Or clamp: batchSize: Math.max(1, n).","Fix the source producing non-positive values (env defaults, empty-length divisions)."],"exampleFix":"// before\nconst batchSize = Number(process.env.BATCH_SIZE ?? 0);\nawait cursor.eachAsync(fn, { batchSize }); // 0 -> TypeError\n\n// after\nconst raw = Number(process.env.BATCH_SIZE ?? NaN);\nconst batchSize = Number.isInteger(raw) && raw >= 1 ? raw : undefined;\nawait cursor.eachAsync(fn, { batchSize }); // undefined = driver default","handlingStrategy":"validation","validationCode":"function toBatchSize(v) {\n  const n = v == null ? NaN : Number(v);\n  return Number.isInteger(n) && n >= 1 ? n : undefined; // undefined = driver default\n}\nawait cursor.eachAsync(fn, { batchSize: toBatchSize(process.env.BATCH_SIZE) });","typeGuard":"function isValidBatchSize(v) {\n  return typeof v === 'number' && Number.isInteger(v) && v >= 1;\n}","tryCatchPattern":null,"preventionTips":["Do not use 0 or -1 as 'use default' sentinels; use undefined/null and normalize.","Validate env-driven numeric config at startup and clamp to minimums.","Log the normalized batch size in ETL jobs so bad config is visible before cursors start."],"tags":["mongoose","cursor","eachasync","batch-size","bounds-check"],"backgroundTag":"invalid-batch-size","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}