flowable/flowable-engine · error · FlowableIllegalArgumentException
The number of job retries must be a non-negative Integer…
Error message
The number of job retries must be a non-negative Integer, but '${retries}' has been provided. What it means
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.
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
Example fix
// before int retries = maxRetries - failureCount; managementService.setTimerJobRetries(jobId, retries); // after int retries = Math.max(0, maxRetries - failureCount); managementService.setTimerJobRetries(jobId, retries);
Defensive patterns
Strategy: validation
Validate before calling
if (retries < 0) {
throw new IllegalArgumentException("retries must be >= 0");
}
managementService.setTimerJobRetries(jobId, retries); Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- A channel key detection value is required for the channel…
- A group or a user is required to create an identity link.
- A group or a user is required to create an identity link.
- A request body was expected when executing the form submit.
- A resource name is mandatory
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/5cbe921b439257cb.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/cmd/SetTimerJobRetriesCmd.java:45
import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
/**
* @author Tijs Rademakers
*/
public class SetTimerJobRetriesCmd implements Command<Void>, Serializable {
private static final long serialVersionUID = 1L;
protected final String jobId;
protected final int retries;
protected JobServiceConfiguration jobServiceConfiguration;
public SetTimerJobRetriesCmd(String jobId, int retries, JobServiceConfiguration jobServiceConfiguration) {
if (jobId == null || jobId.length() < 1) {
throw new FlowableIllegalArgumentException("The job id is mandatory, but '" + jobId + "' has been provided.");
}
if (retries < 0) {
throw new FlowableIllegalArgumentException("The number of job retries must be a non-negative Integer, but '" + retries + "' has been provided.");
}
this.jobId = jobId;
this.retries = retries;
this.jobServiceConfiguration = jobServiceConfiguration;
}
@Override
public Void execute(CommandContext commandContext) {
TimerJobEntity job = jobServiceConfiguration.getTimerJobEntityManager().findById(jobId);
if (job != null) {
job.setRetries(retries);
FlowableEventDispatcher eventDispatcher = jobServiceConfiguration.getEventDispatcher();
if (eventDispatcher != null && eventDispatcher.isEnabled()) {
eventDispatcher.dispatchEvent(FlowableJobEventBuilder.createEntityEvent(FlowableEngineEventType.ENTITY_UPDATED, job),
jobServiceConfiguration.getEngineName());
}View on GitHub (pinned to d6d39ce1c6)