{"record":{"id":"5cbe921b439257cb","repo":"flowable/flowable-engine","slug":"the-number-of-job-retries-must-be-a-non-negative-i-5cbe92","errorCode":null,"errorMessage":"The number of job retries must be a non-negative Integer, but '${retries}' has been provided.","messagePattern":"The number of job retries must be a non-negative Integer, but '(.+?)' has been provided\\.","errorType":"validation","errorClass":"FlowableIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/cmd/SetTimerJobRetriesCmd.java","lineNumber":45,"sourceCode":"import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;\n\n/**\n * @author Tijs Rademakers\n */\npublic class SetTimerJobRetriesCmd implements Command<Void>, Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    protected final String jobId;\n    protected final int retries;\n    protected JobServiceConfiguration jobServiceConfiguration;\n\n    public SetTimerJobRetriesCmd(String jobId, int retries, JobServiceConfiguration jobServiceConfiguration) {\n        if (jobId == null || jobId.length() < 1) {\n            throw new FlowableIllegalArgumentException(\"The job id is mandatory, but '\" + jobId + \"' has been provided.\");\n        }\n        if (retries < 0) {\n            throw new FlowableIllegalArgumentException(\"The number of job retries must be a non-negative Integer, but '\" + retries + \"' has been provided.\");\n        }\n        this.jobId = jobId;\n        this.retries = retries;\n        this.jobServiceConfiguration = jobServiceConfiguration;\n    }\n\n    @Override\n    public Void execute(CommandContext commandContext) {\n        TimerJobEntity job = jobServiceConfiguration.getTimerJobEntityManager().findById(jobId);\n        if (job != null) {\n\n            job.setRetries(retries);\n\n            FlowableEventDispatcher eventDispatcher = jobServiceConfiguration.getEventDispatcher();\n            if (eventDispatcher != null && eventDispatcher.isEnabled()) {\n                eventDispatcher.dispatchEvent(FlowableJobEventBuilder.createEntityEvent(FlowableEngineEventType.ENTITY_UPDATED, job),\n                        jobServiceConfiguration.getEngineName());\n            }","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/cmd/SetTimerJobRetriesCmd.java#L27-L63","documentation":"Flowable throws this FlowableIllegalArgumentException in the SetTimerJobRetriesCmd constructor when the retries argument is negative. Retry counts must be zero or positive because they set how many attempts remain for the timer job; a negative value has no valid meaning in the persistence model.","triggerScenarios":"Calling new SetTimerJobRetriesCmd(jobId, -1, config) or any negative retries value, typically when retries is computed (e.g. maxRetries - failureCount) and the computation underflows below zero.","commonSituations":"Computing remaining retries with an off-by-one decrement loop; passing an unvalidated user/config-provided retry count; arithmetic on int overflow producing a negative value.","solutions":["Clamp the computed value before calling: Math.max(0, retries)","Validate the retries input at the API boundary before constructing the command","Fix the decrement logic that can produce negative counts"],"exampleFix":"// before\nint retries = maxRetries - failureCount;\nmanagementService.setTimerJobRetries(jobId, retries);\n// after\nint retries = Math.max(0, maxRetries - failureCount);\nmanagementService.setTimerJobRetries(jobId, retries);","handlingStrategy":"validation","validationCode":"if (retries < 0) {\n    throw new IllegalArgumentException(\"retries must be >= 0\");\n}\nmanagementService.setTimerJobRetries(jobId, retries);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Clamp computed retry counts with Math.max(0, value)","Never pass raw user/config-provided retry counts without bounds checking","Watch decrement loops that can go negative"],"tags":["validation","flowable","retry-count"],"backgroundTag":"invalid-argument-value","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}