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

API-misuse guard in ExecutionQueryImpl.endOr: endOr() was invoked while no OR block was open (or() was never called on this query), leaving the query's or-state unbalanced and the fluent chain invalid.

Source

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

    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()");
        }

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

    // ordering ////////////////////////////////////////////////////

    @Override
    public ExecutionQueryImpl orderByProcessInstanceId() {
        this.orderProperty = ExecutionQueryProperty.PROCESS_INSTANCE_ID;
        return this;
    }

    @Override
    public ExecutionQueryImpl orderByProcessDefinitionId() {
        this.orderProperty = ExecutionQueryProperty.PROCESS_DEFINITION_ID;

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Ensure endOr() is only invoked when or() was called earlier on the same query
  2. Track open/close state in your builder wrapper before emitting endOr()
  3. Remove stray endOr() calls when the query has no OR criteria

Example fix

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

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

Strategy: validation

Validate before calling

if (builder.isInsideOrBlock()) {
    builder.closeOr();
}

Try / catch

try {
    query.endOr();
} catch (FlowableException e) {
    if (!e.getMessage().contains("endOr() can only be called after calling or()")) throw e;
    // no OR block was open; proceed without closing
}

Prevention

When it happens

Trigger: Calling query.endOr() without ever calling or(); calling endOr() twice after a single or(); or copying fragments of builder code where the or() call is conditionally skipped but endOr() always runs.

Common situations: Query assembly code with conditional logic (if (a) or(); if (b) ...) where the or() branch was not taken; accidental duplicate endOr() in chained builders.

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