flowable/flowable-engine · error · FlowableIllegalArgumentException

Invalid query usage: cannot set both taskAssigneeIds and…

Error message

Invalid query usage: cannot set both taskAssigneeIds and taskAssignee

What it means

taskAssigneeIds refuses to be combined with taskAssignee(String) on the same query object. The two filters are alternative ways to constrain by assignee, and mixing them would create ambiguous query semantics. Flowable enforces the mutual exclusion with this error.

Solutions

  1. Remove the redundant taskAssignee(...) call and use only taskAssigneeIds for assignee filtering.
  2. Make filter composition mutually exclusive: if an assignee-ID list exists, skip the single-assignee criteria.
  3. Use separate query objects when both filters are genuinely needed in different branches.

Example fix

// before
taskQuery.taskAssignee(assignee);
taskQuery.taskAssigneeIds(assigneeIds);

// after
if (assigneeIds != null && !assigneeIds.isEmpty()) {
    taskQuery.taskAssigneeIds(assigneeIds);
} else if (assignee != null) {
    taskQuery.taskAssignee(assignee);
}
Defensive patterns

Strategy: validation

Validate before calling

if (assigneeIds != null && !assigneeIds.isEmpty() && assignee != null) { throw new IllegalStateException("choose one assignee filter"); }

Type guard

boolean conflict = assignee != null && assigneeIds != null && !assigneeIds.isEmpty();

Try / catch

try {
    applyAssigneeFilters(taskQuery, assignee, assigneeIds);
} catch (FlowableIllegalArgumentException e) {
    if (!e.getMessage().contains("cannot set both taskAssigneeIds and taskAssignee")) throw e;
    logger.error("Query builder set conflicting assignee filters", e);
}

Prevention

When it happens

Trigger: Calling taskQuery.taskAssignee("alice").taskAssigneeIds(ids) (or setting them in either order) on the same TaskQuery instance — often when query criteria are assembled dynamically and different code paths each set an assignee constraint.

Common situations: Composing a query from multiple optional filter objects that both set assignee criteria; migrating code from single-assignee to multi-assignee queries while leaving the old call in place; an OR-block or shared query reused across features.

Related errors


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

Appendix: source

Thrown at modules/flowable-task-service/src/main/java/org/flowable/task/service/impl/TaskQueryImpl.java:481

        return this;
    }

    @Override
    public TaskQuery taskAssigneeIds(Collection<String> assigneeIds) {
        if (assigneeIds == null) {
            throw new FlowableIllegalArgumentException("Task assignee list is null");
        }
        if (assigneeIds.isEmpty()) {
            throw new FlowableIllegalArgumentException("Task assignee list is empty");
        }
        for (String assignee : assigneeIds) {
            if (assignee == null) {
                throw new FlowableIllegalArgumentException("None of the given task assignees can be null");
            }
        }

        if (assignee != null) {
            throw new FlowableIllegalArgumentException("Invalid query usage: cannot set both taskAssigneeIds and taskAssignee");
        }
        if (assigneeLike != null) {
            throw new FlowableIllegalArgumentException("Invalid query usage: cannot set both taskAssigneeIds and taskAssigneeLike");
        }
        if (assigneeLikeIgnoreCase != null) {
            throw new FlowableIllegalArgumentException("Invalid query usage: cannot set both taskAssigneeIds and taskAssigneeLikeIgnoreCase");
        }

        if (orActive) {
            currentOrQueryObject.assigneeIds = assigneeIds;
        } else {
            this.assigneeIds = assigneeIds;
        }
        return this;
    }

    @Override
    public TaskQueryImpl taskOwner(String owner) {

View on GitHub (pinned to d6d39ce1c6)