{"record":{"id":"c5c229c76232d3e1","repo":"windmill-labs/windmill","slug":"exit-after-n-jobs-must-be-a-positive-integer-or-0","errorCode":null,"errorMessage":"EXIT_AFTER_N_JOBS must be a positive integer (or 0 to disable), got '{v}'","messagePattern":"EXIT_AFTER_N_JOBS must be a positive integer \\(or 0 to disable\\), got '(.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/windmill-common/src/worker.rs","lineNumber":498,"sourceCode":"    .and_then(|x| x.parse::<bool>().ok())\n    .unwrap_or(false);\n\n    // Features flags:\n    pub static ref DISABLE_FLOW_SCRIPT: bool = std::env::var(\"DISABLE_FLOW_SCRIPT\").ok().is_some_and(|x| x == \"1\" || x == \"true\");\n\n    pub static ref ROOT_STANDALONE_BUNDLE_DIR: String = format!(\"{}/.windmill/standalone_bundle\", std::env::var(\"HOME\").unwrap_or_else(|_| \"/root\".to_string()));\n}\n\nlazy_static::lazy_static! {\n    pub static ref ROOT_CACHE_NOMOUNT_DIR: String = format!(\"{}/cache_nomount/\", *WINDMILL_DIR);\n}\n\n/// Refuses to start on an `EXIT_AFTER_N_JOBS` that does not parse. Silently ignoring it\n/// would leave a worker meant to recycle its environment running forever without doing so,\n/// which is exactly the guarantee the deployment set it for.\npub fn validate_worker_lifecycle_env() -> anyhow::Result<()> {\n    match std::env::var(\"EXIT_AFTER_N_JOBS\") {\n        Ok(v) if !v.is_empty() && v.parse::<u64>().is_err() => Err(anyhow::anyhow!(\n            \"EXIT_AFTER_N_JOBS must be a positive integer (or 0 to disable), got '{v}'\"\n        )),\n        _ => Ok(()),\n    }\n}\n\n/// Whether native mode is forced by the environment (NATIVE_MODE=true env var or WORKER_GROUP=native).\n/// This does NOT account for native_mode set in the DB worker group config — for that, read\n/// `WORKER_CONFIG.native_mode` which combines all sources.\npub fn is_native_mode_from_env() -> bool {\n    *NATIVE_MODE || *WORKER_GROUP == \"native\"\n}\n\n/// True iff this process is configured to act as the production cloud cluster:\n/// `CLOUD_HOSTED=true` AND `BASE_URL`'s host matches `CLOUD_PRODUCTION_HOST`.\n/// Centralized so the API setter, the runtime pull path, and any future cloud-\n/// only feature share one canonical check (rather than re-implementing the\n/// scheme/host parser at each call site).","sourceCodeStart":480,"sourceCodeEnd":516,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/backend/windmill-common/src/worker.rs#L480-L516","documentation":"`validate_worker_lifecycle_env` checks the EXIT_AFTER_N_JOBS environment variable at worker startup. If the variable is set but not parseable as a u64 (empty string counts as unset here and is fine), the worker refuses to start. Fail-fast is deliberate: silently ignoring an unparseable value would leave a worker that was supposed to recycle after N jobs running forever, defeating the deployment's restart guarantee.","triggerScenarios":"Setting `EXIT_AFTER_N_JOBS=ten`, `EXIT_AFTER_N_JOBS=5.0`, `EXIT_AFTER_N_JOBS=\" 10\"`, `EXIT_AFTER_N_JOBS=-3` (negative), or any non-integer value, then starting a worker — `windmill_main` calls the validator and aborts on error.","commonSituations":"Using a float or unit-suffixed value ('50 jobs'); negative numbers assumed valid; shell quoting leaving stray spaces; Helm/k8s templates rendering numbers as strings with formatting; YAML `EXIT_AFTER_N_JOBS: 010` confusion is fine but `\" \"` whitespace-only strings fail parse.","solutions":["Set EXIT_AFTER_N_JOBS to a plain non-negative integer, e.g. `EXIT_AFTER_N_JOBS=100` (0 disables the exit-after-N behavior).","Remove surrounding whitespace/quotes from the value in your env file or manifest.","Check for templating artifacts: rendered values like `100.0` or `nil` must be corrected to integers.","Unset the variable entirely if you do not want lifecycle-based recycling."],"exampleFix":"// before (values.yaml env)\n- name: EXIT_AFTER_N_JOBS\n  value: \"50.0\"\n// after\n- name: EXIT_AFTER_N_JOBS\n  value: \"50\"","handlingStrategy":"validation","validationCode":"const raw = process.env.EXIT_AFTER_N_JOBS;\nif (raw !== undefined && raw !== \"\" && !/^\\d+$/.test(raw)) {\n  throw new Error(`EXIT_AFTER_N_JOBS must be a non-negative integer, got '${raw}'`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  startWorker();\n} catch (e) {\n  if (String(e).includes(\"EXIT_AFTER_N_JOBS\")) {\n    console.error(`Fix EXIT_AFTER_N_JOBS: '${process.env.EXIT_AFTER_N_JOBS}' is not an integer. Use e.g. 100, or 0/unset to disable.`);\n    process.exit(1);\n  }\n  throw e;\n}","preventionTips":["Always set EXIT_AFTER_N_JOBS as a bare integer string, e.g. \"100\".","Quote-free plain values in env files; avoid floats, units, or negatives.","Check rendered k8s/Helm templates for formatting artifacts (\"50.0\", empty strings).","Unset the variable rather than setting it to an unparseable placeholder when recycling is not wanted."],"tags":["configuration","worker","env","validation"],"backgroundTag":"invalid-env-var-format","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}