flowable/flowable-engine · error · FlowableIllegalArgumentException
processInstanceIds is empty
Error message
processInstanceIds is empty
What it means
BulkDeleteHistoricProcessInstancesCmd.execute throws FlowableIllegalArgumentException when the process instance id collection is non-null but empty. An empty list would be a no-op delete, so the engine treats it as an invalid call instead of silently succeeding.
Source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/BulkDeleteHistoricProcessInstancesCmd.java:40
import org.flowable.engine.impl.util.CommandContextUtil;
public class BulkDeleteHistoricProcessInstancesCmd implements Command<Object>, Serializable {
private static final long serialVersionUID = 1L;
protected Collection<String> processInstanceIds;
public BulkDeleteHistoricProcessInstancesCmd(Collection<String> processInstanceIds) {
this.processInstanceIds = processInstanceIds;
}
@Override
public Object execute(CommandContext commandContext) {
if (processInstanceIds == null) {
throw new FlowableIllegalArgumentException("processInstanceIds is null");
}
if (processInstanceIds.isEmpty()) {
throw new FlowableIllegalArgumentException("processInstanceIds is empty");
}
CommandContextUtil.getHistoryManager(commandContext).recordBulkDeleteProcessInstances(processInstanceIds);
return null;
}
}
View on GitHub (pinned to d6d39ce1c6)
Solutions
- Guard with an isEmpty() check before calling; skip the bulk delete when the list is empty.
- Log when the empty case occurs so batch jobs do not look failed when nothing matched.
- Fix upstream query logic if the empty result is unexpected (e.g. wrong historic-level configuration or time window).
- Only catch FlowableIllegalArgumentException if an empty batch is legitimately expected and ignorable.
Example fix
// before
historyService.bulkDeleteHistoricProcessInstances(ids); // may be empty
// after
if (ids != null && !ids.isEmpty()) {
historyService.bulkDeleteHistoricProcessInstances(ids);
} else {
logger.info("No historic process instances to delete");
} Defensive patterns
Strategy: validation
Validate before calling
if (processInstanceIds == null || processInstanceIds.isEmpty()) {
logger.info("Nothing to delete; skipping bulk historic delete");
return;
} Try / catch
try {
historyService.bulkDeleteHistoricProcessInstances(ids);
} catch (FlowableIllegalArgumentException e) {
if (e.getMessage().contains("is empty")) {
logger.info("Empty id list; nothing deleted");
} else {
throw e;
}
} Prevention
- Always check isEmpty() before bulk operations.
- Make batch jobs log empty batches as informational, not errors.
- Review upstream query filters when empty results are unexpected.
- Centralize collection guards in a utility method for bulk engine calls.
When it happens
Trigger: Calling the bulk delete historic process instances API with an empty List (e.g. a query for completed instances returned nothing and the result was passed straight through to the bulk delete).
Common situations: Batch jobs that gather finished instance ids then delete their history — on the first run or when no instances match the filter, the list is empty and the call fails; filtering criteria too narrow; scheduler runs before data exists.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- processInstanceIds is null
- Could not find an app definition with id '<appDefinitionId>
- Could not find a deployment with id '<deploymentId>
- Empty appsDefinitionIds
- ids is an empty collection
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/8ac6a86a61da87b2.
Report an issue: GitHub.