flowable/flowable-engine · error · org.flowable.common.engine.api.FlowableIllegalArgumentException

processInstanceIds is empty

Error message

processInstanceIds is empty

What it means

HistoricVariableInstanceQueryImpl.processInstanceIds requires a non-null, non-empty collection and throws FlowableIllegalArgumentException when the collection is null or empty. An empty IN clause would generate invalid SQL, so the builder validates eagerly. This error occurs at query-construction time.

Source

Thrown at modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/HistoricVariableInstanceQueryImpl.java:92

    @Override
    public HistoricVariableInstanceQuery id(String id) {
        this.id = id;
        return this;
    }

    @Override
    public HistoricVariableInstanceQueryImpl processInstanceId(String processInstanceId) {
        if (processInstanceId == null) {
            throw new FlowableIllegalArgumentException("processInstanceId is null");
        }
        this.processInstanceId = processInstanceId;
        return this;
    }

    @Override
    public HistoricVariableInstanceQuery processInstanceIds(Collection<String> processInstanceIds) {
        if (processInstanceIds == null || processInstanceIds.isEmpty()) {
            throw new FlowableIllegalArgumentException("processInstanceIds is empty");
        }
        this.processInstanceIds = processInstanceIds;
        return this;
    }

    @Override
    public HistoricVariableInstanceQueryImpl executionId(String executionId) {
        if (executionId == null) {
            throw new FlowableIllegalArgumentException("Execution id is null");
        }
        if (excludeLocalVariables) {
            throw new FlowableIllegalArgumentException("Cannot use executionId together with excludeLocalVariables");
        }
        this.executionId = executionId;
        return this;
    }

    @Override

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Skip the query (or return an empty result directly) when the collection is empty
  2. Only add the processInstanceIds clause when the collection has elements
  3. Ensure the upstream query that produces the ids actually matches expected data
  4. Defensively default to a sentinel if querying all instances is intended (or use no clause)

Example fix

// before
query.processInstanceIds(ids);
// after
if (ids != null && !ids.isEmpty()) {
    query.processInstanceIds(ids);
} else {
    return Collections.emptyList();
}
Defensive patterns

Strategy: validation

Validate before calling

if (ids == null || ids.isEmpty()) {
    return Collections.emptyList(); // nothing to query
}
query.processInstanceIds(ids);

Type guard

static boolean hasIds(Collection<String> ids) {
    return ids != null && !ids.isEmpty();
}

Try / catch

try {
    query.processInstanceIds(ids);
} catch (org.flowable.common.engine.api.FlowableIllegalArgumentException e) {
    // return empty result set for empty input
}

Prevention

When it happens

Trigger: Calling createHistoricVariableInstanceQuery().processInstanceIds(Collections.emptyList()) or processInstanceIds(null), commonly when the id list comes from an upstream query that matched nothing.

Common situations: Batch lookups where the previous query returned zero rows; request parameters with no selected instances; building queries in loops that start with an empty accumulator.

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/aaadb899e76f976a. Report an issue: GitHub.