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

  1. Check groupIds.isEmpty() before calling involvedGroups and skip the filter when empty.
  2. Fall back to another criterion (e.g. involvedUser) when the user has no group memberships.
  3. Decide the intended semantics: an empty set likely means 'no restriction', so omit the filter.
  4. 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

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.