flowable/flowable-engine · error · ActivitiIllegalArgumentException

Candidate group list is null

Error message

Candidate group list is null

What it means

HistoricTaskInstanceQueryImpl.taskCandidateGroupIn(List<String>) rejects a null list with ActivitiIllegalArgumentException. Passing null means no group criterion could be built, so the engine fails fast rather than issuing a malformed query.

Source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/HistoricTaskInstanceQueryImpl.java:1099

            throw new ActivitiIllegalArgumentException("Candidate group is null");
        }

        if (candidateGroups != null) {
            throw new ActivitiIllegalArgumentException("Invalid query usage: cannot set both candidateGroup and candidateGroupIn");
        }

        if (inOrStatement) {
            this.currentOrQueryObject.candidateGroup = candidateGroup;
        } else {
            this.candidateGroup = candidateGroup;
        }
        return this;
    }

    @Override
    public HistoricTaskInstanceQuery taskCandidateGroupIn(List<String> candidateGroups) {
        if (candidateGroups == null) {
            throw new ActivitiIllegalArgumentException("Candidate group list is null");
        }

        if (candidateGroups.isEmpty()) {
            throw new ActivitiIllegalArgumentException("Candidate group list is empty");
        }

        if (candidateGroup != null) {
            throw new ActivitiIllegalArgumentException("Invalid query usage: cannot set both candidateGroupIn and candidateGroup");
        }

        if (inOrStatement) {
            this.currentOrQueryObject.candidateGroups = candidateGroups;
        } else {
            this.candidateGroups = candidateGroups;
        }
        return this;
    }

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Initialize the list (Collections.emptyList() if you also handle the empty case) before calling, or skip the call when null.
  2. Make the upstream resolver return an empty list instead of null.
  3. If the groups are mandatory, fail in your own layer with a clearer error before touching the query.

Example fix

// before
List<String> groups = fetchGroups(tenantId); // may return null
query.taskCandidateGroupIn(groups); // throws when null

// after
List<String> groups = fetchGroups(tenantId);
if (groups != null && !groups.isEmpty()) {
    query.taskCandidateGroupIn(groups);
}
Defensive patterns

Strategy: validation

Validate before calling

// Java
if (candidateGroups != null && !candidateGroups.isEmpty()) {
    query.taskCandidateGroupIn(candidateGroups);
}

Try / catch

try {
    query.taskCandidateGroupIn(groups);
} catch (org.activiti.engine.ActivitiIllegalArgumentException e) {
    // skip filter or propagate a clearer domain error
}

Prevention

When it happens

Trigger: Calling taskCandidateGroupIn(null), e.g. when the group list variable was never initialized or a lookup returned null.

Common situations: Group list fetched from an external source (LDAP, DB, config) that returned null on failure; uninitialized field in a filter DTO passed straight through.

Related errors


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