flowable/flowable-engine · error · FlowableException

the query is already in an or statement

Error message

the query is already in an or statement

What it means

ExecutionQueryImpl.or() opens an OR block in the query but cannot be nested; calling or() while already inside an or statement throws FlowableException. Flowable query API supports only one flat or() ... endOr() block per query.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/ExecutionQueryImpl.java:980

    @Override
    public ExecutionQuery startedBy(String userId) {
        if (userId == null) {
            throw new FlowableIllegalArgumentException("user id is null");
        }
        
        if (inOrStatement) {
            currentOrQueryObject.startedBy = userId;
        } else {
            this.startedBy = userId;
        }

        return this;
    }
    
    @Override
    public ExecutionQuery or() {
        if (inOrStatement) {
            throw new FlowableException("the query is already in an or statement");
        }

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

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

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Call or() once, then chain multiple conditions inside the block, and close with endOr()
  2. Track an inOr flag in your builder so or() is only emitted when not already inside an OR block
  3. Refactor conditions that require nested ORs into two separate queries and merge/union the results in application code

Example fix

// before
query.or().processDefinitionKey("k1").or().processDefinitionKey("k2").endOr();

// after
query.or().processDefinitionKey("k1").processDefinitionKey("k2").endOr();
Defensive patterns

Strategy: validation

Validate before calling

if (!builder.isInsideOrBlock()) {
    builder.openOr();
}

Try / catch

try {
    query.or();
} catch (FlowableException e) {
    if (!e.getMessage().contains("already in an or statement")) throw e;
    // continue adding conditions inside the existing OR block
}

Prevention

When it happens

Trigger: Calling query.or() twice without an intervening endOr(), e.g. building criteria in a loop that adds or() per condition, or concatenating two query-builder fragments that each start with or().

Common situations: Dynamic query builders that append or() unconditionally for each optional filter; reusable filter helper methods that each call or() internally.

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