{"record":{"id":"840d0014e5e0cbb9","repo":"dubinc/dub","slug":"maxbatches-must-be-greater-than-0","errorCode":null,"errorMessage":"maxBatches must be greater than 0.","messagePattern":"maxBatches must be greater than 0\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/utils/src/functions/process-in-batches.ts","lineNumber":9,"sourceCode":"// Runs a batch operation up to `maxBatches` times in a single invocation.\n// Use in cron jobs/background workers when you need to process a large set of rows without exceeding the function timeout.\n// Each call to `processBatch` should itself be limited (e.g. Prisma `updateMany`/`deleteMany` with `limit`)\nexport async function processInBatches(\n  maxBatches: number,\n  processBatch: () => Promise<{ count: number }>,\n): Promise<{ hasMore: boolean }> {\n  if (maxBatches <= 0) {\n    throw new Error(\"maxBatches must be greater than 0.\");\n  }\n\n  for (let batch = 0; batch < maxBatches; batch++) {\n    const { count } = await processBatch();\n\n    if (count === 0) {\n      return {\n        hasMore: false,\n      };\n    }\n  }\n\n  // Exhausted allowed batches. There may still be work left.\n  return {\n    hasMore: true,\n  };\n}\n","sourceCodeStart":1,"sourceCodeEnd":27,"githubUrl":"https://github.com/dubinc/dub/blob/f216b94a24ca5a0a48c6543ee10392c9006c8b75/packages/utils/src/functions/process-in-batches.ts#L1-L27","documentation":"processInBatches validates its first argument and throws this Error immediately if maxBatches is not a positive number. maxBatches caps how many batch iterations will run, so a zero or negative value is meaningless and treated as a programmer error rather than a runtime condition.","triggerScenarios":"Calling processInBatches(0, fn), processInBatches(-1, fn), or passing a variable computed to <= 0 (e.g. Math.ceil(total/size) where total is 0 or size exceeds total).","commonSituations":"Computing maxBatches from an empty dataset (total items = 0), passing an uninitialized/NaN variable that coerces into the check, or a config value defaulting to 0.","solutions":["Guard the call site: only invoke processInBatches when maxBatches > 0.","Check where maxBatches is computed — Math.ceil(total/batchSize) is 0 when total is 0; skip the call entirely for empty datasets.","Clamp the value: Math.max(1, computedBatches) if you always want at least one attempt.","Validate the input with an assertion before the call for clearer stack traces."],"exampleFix":"// before\nconst batches = Math.ceil(totalItems / BATCH_SIZE);\nawait processInBatches(batches, processBatch);\n// after\nconst batches = Math.ceil(totalItems / BATCH_SIZE);\nif (batches > 0) {\n  await processInBatches(batches, processBatch);\n}","handlingStrategy":"validation","validationCode":"function assertPositive(n: number, name: string): void {\n  if (!Number.isInteger(n) || n <= 0) {\n    throw new RangeError(`${name} must be a positive integer, got ${n}`);\n  }\n}\nassertPositive(maxBatches, 'maxBatches');\nawait processInBatches(maxBatches, processBatch);","typeGuard":"function isValidMaxBatches(n: unknown): n is number {\n  return typeof n === 'number' && Number.isInteger(n) && n > 0;\n}","tryCatchPattern":"try {\n  await processInBatches(maxBatches, processBatch);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('maxBatches must be greater than 0')) {\n    console.error(`Bad maxBatches value: ${maxBatches}`);\n  } else {\n    throw e;\n  }\n}","preventionTips":["Skip the call entirely when the dataset is empty (totalItems === 0).","Compute maxBatches with Math.max(1, Math.ceil(total / size)) if one attempt is always acceptable.","Guard against NaN — NaN <= 0 is false, so it would slip past; validate Number.isFinite first.","Unit-test batch-count computation with zero-item inputs."],"tags":["validation","arguments","batching"],"backgroundTag":"invalid-argument","analyzedSha":"f216b94a24ca5a0a48c6543ee10392c9006c8b75","analyzedAt":"2026-08-31T18:35:50.395Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}