flowable/flowable-engine · error · FlowableException

endOr() can only be called after calling or()

Error message

endOr() can only be called after calling or()

What it means

EventSubscriptionQuery.endOr() closes an OR block opened by or(). Flowable throws FlowableException when endOr() is called without an active OR statement, keeping the or()/endOr() bracket strictly paired. The query object tracks this with the inOrStatement flag at EventSubscriptionQueryImpl.java:486.

Source

Thrown at modules/flowable-eventsubscription-service/src/main/java/org/flowable/eventsubscription/service/impl/EventSubscriptionQueryImpl.java:486

        return this;
    }

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

        inOrStatement = true;
        currentOrQueryObject = new EventSubscriptionQueryImpl();
        orQueryObjects.add(currentOrQueryObject);
        return this;
    }

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

        inOrStatement = false;
        currentOrQueryObject = null;
        return this;
    }

    @Override
    public EventSubscriptionQuery orderById() {
        return orderBy(EventSubscriptionQueryProperty.ID);
    }

    @Override
    public EventSubscriptionQuery orderByExecutionId() {
        return orderBy(EventSubscriptionQueryProperty.EXECUTION_ID);
    }

    @Override

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Ensure endOr() is only called after a successful or() and exactly once per OR block
  2. Make or()/endOr() calls unconditional and adjacent, adding predicates between them
  3. Remove the stray endOr() call if no OR grouping is needed

Example fix

// before
if (useOr) {
    query.or();
}
query.endOr();

// after
if (useOr) {
    query.or();
    query.eventSubscriptionType("message");
    query.endOr();
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!orOpened) {
    throw new IllegalStateException("endOr() called without or()");
}

Try / catch

try {
    query.endOr();
} catch (FlowableException e) {
    log.error("endOr() without or(): {}", e.getMessage());
    throw e;
}

Prevention

When it happens

Trigger: Calling query.endOr() with no preceding or(), calling endOr() twice, or calling endOr() after an early return/control-flow path already closed the block.

Common situations: Refactored query builders where the or() call was removed or made conditional but the matching endOr() remained; exception-handling paths that skip or() but still reach endOr() in a finally-style cleanup.

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