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

PlanItemInstanceQueryImpl.or() throws FlowableIllegalArgumentException if the query is already inside an or() block (inOrStatement is true). or() / endOr() must strictly alternate; nested or-expressions are not supported.

Source

Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/PlanItemInstanceQueryImpl.java:1156

            variableExists(name, false);
        }
        return this;
    }

    @Override
    public PlanItemInstanceQuery caseVariableNotExists(String name) {
        if (inOrStatement) {
            this.currentOrQueryObject.variableNotExists(name, false);
        } else {
            variableNotExists(name, false);
        }
        return this;
    }

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

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

    @Override
    public PlanItemInstanceQuery 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 starting a new or() block.
  2. Track whether the query is already in an or statement and only call or() at the top level.
  3. Restructure the predicate: combine conditions inside a single or() block instead of nesting.

Example fix

// before
query.or().planItemInstanceDefinitionId("a");
query.or().planItemInstanceDefinitionId("b"); // wrong: already in or
// after
query.or().planItemInstanceDefinitionId("a").planItemInstanceDefinitionId("b").endOr();
Defensive patterns

Strategy: validation

Validate before calling

boolean inOr = query.toString().contains("OR"); // track or() state in your own builder wrapper instead

Try / catch

try { query.or(); } catch (FlowableIllegalArgumentException e) { throw new IllegalStateException("Nested or() not allowed; balance or()/endOr()", e); }

Prevention

When it happens

Trigger: Calling or() twice without an intervening endOr(); a shared query-builder method that calls or() while the caller already opened an or block.

Common situations: Programmatic query assembly from a DSL or rules engine where an or() call is generated inside an existing or block; refactored helper methods that append or() clauses unconditionally.

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