flowable/flowable-engine · error · FlowableIllegalArgumentException

The query is already in an or statement

Error message

The query is already in an or statement

What it means

HistoricPlanItemInstanceQueryImpl.or() throws FlowableIllegalArgumentException if the query is already inside an or block (inOrStatement is true). The or()/endOr() API only supports a single level of OR grouping, so nested or() calls are rejected. This prevents accidentally building an OR-of-OR query the engine cannot express.

Source

Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/history/HistoricPlanItemInstanceQueryImpl.java:812

    @Override
    public HistoricPlanItemInstanceQuery orderByTerminatedTime() {
        return orderBy(HistoricPlanItemInstanceQueryProperty.TERMINATED_TIME);
    }

    @Override
    public HistoricPlanItemInstanceQuery orderByExitTime() {
        return orderBy(HistoricPlanItemInstanceQueryProperty.EXIT_TIME);
    }

    @Override
    public HistoricPlanItemInstanceQuery orderByName() {
        return orderBy(HistoricPlanItemInstanceQueryProperty.NAME);
    }

    @Override
    public HistoricPlanItemInstanceQuery or() {
        if (inOrStatement) {
            throw new FlowableIllegalArgumentException("The query is already in an or statement");
        }

        inOrStatement = true;
        if (commandContext != null) {
            currentOrQueryObject = new HistoricPlanItemInstanceQueryImpl(commandContext);
        } else {
            currentOrQueryObject = new HistoricPlanItemInstanceQueryImpl(commandExecutor);
        }
        orQueryObjects.add(currentOrQueryObject);
        return this;
    }

    @Override
    public HistoricPlanItemInstanceQuery endOr() {
        if (!inOrStatement) {
            throw new FlowableIllegalArgumentException("endOr() can only be called after calling or()");
        }

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Call endOr() before calling or() again
  2. Restructure the query so all OR conditions are within a single or()...endOr() block
  3. If multiple independent OR groups are needed, run separate queries and merge results
  4. Use a flag or loop restructure so or() is invoked exactly once per query

Example fix

// before
query.or().planItemInstanceName("a").or().planItemInstanceName("b"); // throws
// after
query.or().planItemInstanceName("a").planItemInstanceName("b").endOr();
Defensive patterns

Strategy: validation

Validate before calling

boolean canStartOrBlock(HistoricPlanItemInstanceQuery query) {
    // track your own flag; the query does not expose inOrStatement
    return !orBlockOpen;
}

Try / catch

try {
    query.or();
} catch (FlowableIllegalArgumentException e) {
    // already inside an or() block — close it with endOr() first or skip this or() call
}

Prevention

When it happens

Trigger: Calling or() twice without an intervening endOr(), e.g. chaining or() ... or() while building the query, or re-entering or() due to a loop over filter conditions that each call or().

Common situations: Building dynamic queries in loops where each condition calls or() but endOr() is only called once; copy-pasting query-builder code from examples that already used or().

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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