flowable/flowable-engine · error · FlowableIllegalArgumentException

Set of process instance ids is empty

Error message

Set of process instance ids is empty

What it means

HistoricActivityInstanceQueryImpl.processInstanceIds(Set<String>) also rejects an empty set with FlowableIllegalArgumentException. An empty set would translate to an impossible SQL IN () clause, so Flowable fails fast during query construction.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/HistoricActivityInstanceQueryImpl.java:98

    @Override
    public List<HistoricActivityInstance> executeList(CommandContext commandContext) {
        return CommandContextUtil.getHistoricActivityInstanceEntityManager(commandContext).findHistoricActivityInstancesByQueryCriteria(this);
    }

    @Override
    public HistoricActivityInstanceQueryImpl processInstanceId(String processInstanceId) {
        this.processInstanceId = processInstanceId;
        return this;
    }

    @Override
    public HistoricActivityInstanceQueryImpl processInstanceIds(Set<String> processInstanceIds) {
        if (processInstanceIds == null) {
            throw new FlowableIllegalArgumentException("Set of process instance ids is null");
        }
        if (processInstanceIds.isEmpty()) {
            throw new FlowableIllegalArgumentException("Set of process instance ids is empty");
        }
        this.processInstanceIds = processInstanceIds;
        return this;
    }

    @Override
    public HistoricActivityInstanceQueryImpl executionId(String executionId) {
        this.executionId = executionId;
        return this;
    }

    @Override
    public HistoricActivityInstanceQueryImpl processDefinitionId(String processDefinitionId) {
        this.processDefinitionId = processDefinitionId;
        return this;
    }

    @Override

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Check !ids.isEmpty() before calling processInstanceIds; skip the filter or short-circuit to an empty result
  2. Return an empty result early in application code when no ids were selected, avoiding the DB query entirely
  3. Default to a non-filtering query when the set is empty, if 'all instances' is the intended meaning

Example fix

// before
Set<String> ids = selectedIds.stream().filter(this::valid).collect(toSet());
query.processInstanceIds(ids);

// after
Set<String> ids = selectedIds.stream().filter(this::valid).collect(toSet());
if (!ids.isEmpty()) {
    query.processInstanceIds(ids);
} else {
    return Collections.emptyList();
}
Defensive patterns

Strategy: validation

Validate before calling

if (ids != null && !ids.isEmpty()) {
    query.processInstanceIds(ids);
} else {
    return Collections.emptyList();
}

Type guard

boolean isNonEmptySet(Set<String> s) { return s != null && !s.isEmpty(); }

Try / catch

try {
    query.processInstanceIds(ids);
} catch (FlowableIllegalArgumentException e) {
    if (!e.getMessage().contains("Set of process instance ids is empty")) throw e;
    // short-circuit: no ids selected means no results
}

Prevention

When it happens

Trigger: Calling processInstanceIds(new HashSet<>()) or a set filtered down to zero elements (e.g. user selected no instances, or all ids were filtered out before the call).

Common situations: UI-driven reports where the user deselects all items; batch jobs whose id list is empty because no instances matched a prior step.

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


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/f8e9663ffe03a5d7. Report an issue: GitHub.