flowable/flowable-engine · error · FlowableIllegalArgumentException
groupIds are empty
Error message
groupIds are empty
What it means
HistoricCaseInstanceQueryImpl.involvedGroups(Set<String>) throws FlowableIllegalArgumentException("groupIds are empty") when the set is non-null but has no elements. An empty IN-clause would be invalid SQL or silently match nothing, so the library rejects it explicitly — the null check at HistoricCaseInstanceQueryImpl.java:1127 handles null, and the isEmpty check at line 1130 handles empty sets.
Source
Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/history/HistoricCaseInstanceQueryImpl.java:1130
}
if (identityLinkType == null) {
throw new FlowableIllegalArgumentException("identityLinkType is null");
}
if (inOrStatement) {
this.currentOrQueryObject.involvedGroupIdentityLink = new IdentityLinkQueryObject(null, groupId, identityLinkType);
} else {
this.involvedGroupIdentityLink = new IdentityLinkQueryObject(null, groupId, identityLinkType);
}
return this;
}
@Override
public HistoricCaseInstanceQuery involvedGroups(Set<String> groupIds) {
if (groupIds == null) {
throw new FlowableIllegalArgumentException("groupIds are null");
}
if (groupIds.isEmpty()) {
throw new FlowableIllegalArgumentException("groupIds are empty");
}
if (inOrStatement) {
this.currentOrQueryObject.involvedGroups = groupIds;
} else {
this.involvedGroups = groupIds;
}
return this;
}
@Override
public HistoricCaseInstanceQuery or() {
if (inOrStatement) {
throw new FlowableException("the query is already in an or statement");
}
inOrStatement = true;
if (commandContext != null) {
currentOrQueryObject = new HistoricCaseInstanceQueryImpl(commandContext, cmmnEngineConfiguration);View on GitHub (pinned to d6d39ce1c6)
Solutions
- Check groupIds.isEmpty() before calling involvedGroups and skip the filter when empty.
- Fall back to another criterion (e.g. involvedUser) when the user has no group memberships.
- Decide the intended semantics: an empty set likely means 'no restriction', so omit the filter.
- Guard shared query-builder helpers so they never pass empty collections into involvedGroups.
Example fix
// before
query.involvedGroups(groupIds); // groupIds may be empty
// after
if (groupIds != null && !groupIds.isEmpty()) {
query.involvedGroups(groupIds);
} else {
// skip group involvement filter or apply alternate criteria
} Defensive patterns
Strategy: validation
Validate before calling
if (groupIds == null || groupIds.isEmpty()) {
// skip the group involvement filter entirely
return query;
}
query.involvedGroups(groupIds); Type guard
boolean shouldApplyGroupFilter(Set<String> s) {
return s != null && !s.isEmpty();
} Try / catch
try {
query.involvedGroups(groupIds);
} catch (FlowableIllegalArgumentException e) {
if ("groupIds are empty".equals(e.getMessage())) {
// proceed without the group filter or apply alternate criteria
} else {
throw e;
}
} Prevention
- Always guard against both null AND empty before involvedGroups
- Decide semantics for empty membership (no filter vs. no results) up front
- Prefer skipping the filter when the user has no groups
- Cover empty-collection cases in query-builder unit tests
When it happens
Trigger: Calling involvedGroups(new HashSet<>()) or involvedGroups(Set.of()) — i.e. any empty set — on a HistoricCaseInstanceQuery before execution.
Common situations: User belongs to no groups so the membership lookup legitimately returns an empty set; filtering groups by tenant/role yielded zero matches; code unconditionally adds the filter without checking membership results.
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/735b54139841cb20.
Report an issue: GitHub.