{"record":{"id":"42572751a1e2b32f","repo":"n8n-io/n8n","slug":"maxconcurrentpasses-must-be-a-positive-integer-go","errorCode":null,"errorMessage":"maxConcurrentPasses must be a positive integer, got ${maxConcurrentPasses}","messagePattern":"maxConcurrentPasses must be a positive integer, got (.+?)","errorType":"validation","errorClass":"InvalidLifecycleOptionsError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/scheduler/src/core/factory.ts","lineNumber":183,"sourceCode":"\t\tif (!(Number.isFinite(value) && value > 0)) {\n\t\t\tthrow new InvalidLifecycleOptionsError(\n\t\t\t\t`${key} must be a positive number of seconds, got ${value}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tif (\n\t\tlifecycleOptions.concurrencyMode !== 'sequential' &&\n\t\tlifecycleOptions.concurrencyMode !== 'concurrent'\n\t) {\n\t\tthrow new InvalidLifecycleOptionsError(\n\t\t\t`concurrencyMode must be 'sequential' or 'concurrent', got ${String(lifecycleOptions.concurrencyMode)}`,\n\t\t);\n\t}\n\n\tconst { maxConcurrentPasses } = lifecycleOptions;\n\tif (!(Number.isInteger(maxConcurrentPasses) && maxConcurrentPasses >= 1)) {\n\t\tthrow new InvalidLifecycleOptionsError(\n\t\t\t`maxConcurrentPasses must be a positive integer, got ${maxConcurrentPasses}`,\n\t\t);\n\t}\n\n\tconst emit = (level: SchedulerEventLevel, message: string, context: Record<string, unknown>) => {\n\t\t// The sink is the reporting channel itself: if it throws (a broken logger),\n\t\t// there is nothing left to report through, and the pass that emitted must\n\t\t// not be broken by its own observability.\n\t\ttry {\n\t\t\tonEvent?.({ level, message, context });\n\t\t} catch {\n\t\t\t// Deliberately swallowed; see above.\n\t\t}\n\t};\n\tconst described = (error: unknown) => ensureError(error).message;\n\n\t// Derived, not caller-chosen: the materializer must record a job's occurrences\n\t// early enough that the executor still has them in hand when it needs to fire.","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/scheduler/src/core/factory.ts#L165-L201","documentation":"Thrown by createScheduler when lifecycleOptions.maxConcurrentPasses is not an integer >= 1. The value bounds how many scheduler passes (materializer/executor) may run at once; zero would never make progress, a fraction is meaningless for a count, and a negative value is invalid.","triggerScenarios":"Passing maxConcurrentPasses: 0, 1.5, -1, NaN, or a non-integer from a float config. Number.isInteger(maxConcurrentPasses) && maxConcurrentPasses >= 1 must hold.","commonSituations":"A CPU-count heuristic that returns 0 on a single-core/limited container (nproc - 1); a config expressed as a fraction of cores; NaN from parsing an unset env var; floats introduced by a percentage computation.","solutions":["Set maxConcurrentPasses to an integer >= 1, e.g. Math.max(1, Math.floor(cpuCount)).","Guard the CPU-count heuristic so it never returns below 1.","Validate with Number.isInteger in your config loader.","Omit the key to accept the DEFAULT_LIFECYCLE_OPTIONS value."],"exampleFix":"// before\ncreateScheduler({ lifecycle: { maxConcurrentPasses: os.cpus().length - 1 } }); // 0 on 1-core -> throws\n\n// after - floor and clamp to a positive integer\nconst maxConcurrentPasses = Math.max(1, Math.floor(Number(process.env.SCHED_MAX_PASSES) || os.cpus().length));\ncreateScheduler({ lifecycle: { maxConcurrentPasses } });","handlingStrategy":"validation","validationCode":"function normalizeMaxPasses(raw: unknown): number {\n  const n = Math.floor(Number(raw));\n  if (!Number.isInteger(n) || n < 1) return 1;\n  return n;\n}\n\nconst maxConcurrentPasses = normalizeMaxPasses(process.env.SCHED_MAX_PASSES ?? os.cpus().length);","typeGuard":"function isPositiveInteger(v: unknown): v is number {\n  return typeof v === 'number' && Number.isInteger(v) && v >= 1;\n}","tryCatchPattern":null,"preventionTips":["Clamp CPU-count heuristics with Math.max(1, ...) so a 1-core container cannot yield 0.","Round before passing: floor any float that snuck in from a percentage computation.","Unit-test the config loader with 0, 1.5, -1, NaN, undefined.","Default to a known-safe integer when the source value is invalid."],"tags":["scheduler","configuration","validation","numeric"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}