{"record":{"id":"8313fe28dbcc6c16","repo":"payloadcms/payload","slug":"jobs-processinglease-safetybuffer-must-be-non-ne","errorCode":null,"errorMessage":"`jobs.processingLease.safetyBuffer` must be non-negative and less than `jobs.processingLease.duration`.","messagePattern":"`jobs\\.processingLease\\.safetyBuffer` must be non-negative and less than `jobs\\.processingLease\\.duration`\\.","errorType":"http","errorClass":"InvalidConfiguration","httpStatus":500,"severity":"error","filePath":"packages/payload/src/config/sanitize.ts","lineNumber":255,"sourceCode":"    minWidth: 'x-small',\n  })\n  dashboard.defaultLayout ??= [\n    {\n      widgetSlug: 'collections',\n      width: 'full',\n    } satisfies WidgetInstance,\n    {\n      widgetSlug: 'activity',\n      width: 'small',\n    } satisfies WidgetInstance,\n  ]\n}\n\nexport const sanitizeConfig = (incomingConfig: Config): SanitizedConfig => {\n  const configWithDefaults = addDefaultsToConfig(incomingConfig)\n  const { duration, safetyBuffer } = configWithDefaults.jobs!.processingLease!\n  if (!(safetyBuffer! >= 0 && safetyBuffer! < duration!)) {\n    throw new InvalidConfiguration(\n      '`jobs.processingLease.safetyBuffer` must be non-negative and less than `jobs.processingLease.duration`.',\n    )\n  }\n\n  const config: Partial<SanitizedConfig> = sanitizeAdminConfig(configWithDefaults)\n\n  if (!config.endpoints) {\n    config.endpoints = []\n  }\n\n  if (configWithDefaults.collections?.some(({ upload }) => upload)) {\n    config.endpoints.push(uploadInstructionsEndpoint, ...stagedUploadEndpoints)\n  }\n\n  for (const endpoint of authRootEndpoints) {\n    config.endpoints.push(endpoint)\n  }\n","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/payloadcms/payload/blob/00c58b35c0ed348ddc22daabf467b139727214fd/packages/payload/src/config/sanitize.ts#L237-L273","documentation":"Thrown at sanitize time when `jobs.processingLease.safetyBuffer` is negative or not strictly less than `jobs.processingLease.duration`. The lease model lets a worker claim a job for `duration` ms and reserves `safetyBuffer` ms of slack before another worker can reclaim a stalled job; an invalid relationship between the two would cause double-execution or premature requeueing.","triggerScenarios":"Explicitly setting `jobs.processingLease.safetyBuffer` greater than `duration`; setting a negative safetyBuffer; copying values from another config without recalculating.","commonSituations":"Tuning job queue reliability settings; long-running tasks prompting an increase of `duration` while leaving `safetyBuffer` larger; misreading the units (both are milliseconds).","solutions":["Ensure `0 <= safetyBuffer < duration` (both in milliseconds), e.g. `duration: 90_000, safetyBuffer: 15_000`.","If unsure, remove both options and let Payload apply its defaults.","Re-check after any change to job/queue config in plugins."],"exampleFix":"// before\njobs: { processingLease: { duration: 10_000, safetyBuffer: 30_000 } }\n// after\njobs: { processingLease: { duration: 90_000, safetyBuffer: 15_000 } }","handlingStrategy":"validation","validationCode":"const { duration = 90_000, safetyBuffer = 15_000 } = config.jobs?.processingLease ?? {}\nif (!(safetyBuffer >= 0 && safetyBuffer < duration)) {\n  throw new Error('jobs.processingLease.safetyBuffer must be >= 0 and < duration')\n}","typeGuard":"function isValidLease(l: { duration?: number; safetyBuffer?: number }): boolean {\n  const d = l.duration ?? 90_000, s = l.safetyBuffer ?? 15_000\n  return s >= 0 && s < d\n}","tryCatchPattern":null,"preventionTips":["Prefer Payload defaults unless you have measured job runtime.","Both values are milliseconds — re-check units after copying.","When raising `duration`, ensure `safetyBuffer` stays below it."],"tags":["config","jobs","queue","sanitize","buildconfig"],"backgroundTag":null,"analyzedSha":"00c58b35c0ed348ddc22daabf467b139727214fd","analyzedAt":"2026-08-12T20:45:03.758Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}