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

FlowableException thrown by JobQueryImpl.or() when the query is already inside an or() block. Flowable supports only a single or() segment per query; a second nested or() call has no well-defined semantics, so it is rejected. The fix is to finish the or-block (with endOr()) and start a new query, or merge the conditions inside the existing block.

Source

Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/JobQueryImpl.java:563

            this.onlyUnlocked = true;
        }
        return this;
    }

    @Override
    public JobQuery withoutScopeType() {
        if (inOrStatement) {
            this.currentOrQueryObject.withoutScopeType = true;
        } else {
            this.withoutScopeType = true;
        }
        return this;
    }

    @Override
    public JobQuery or() {
        if (inOrStatement) {
            throw new FlowableException("the query is already in an or statement");
        }
        inOrStatement = true;
        if (commandContext != null) {
            currentOrQueryObject = new JobQueryImpl(commandContext, jobServiceConfiguration);
        } else {
            currentOrQueryObject = new JobQueryImpl(commandExecutor, jobServiceConfiguration);
        }
        orQueryObjects.add(currentOrQueryObject);
        return this;
    }

    @Override
    public JobQuery endOr() {
        if (!inOrStatement) {
            throw new FlowableException("endOr() can only be called after calling or()");
        }
        inOrStatement = false;
        currentOrQueryObject = null;

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Call or() only once per query and place all OR-ed conditions before endOr().
  2. Ensure every or() is paired with endOr() before any further or() call.
  3. Refactor filter helpers to detect or-state (or reuse the current or-query object) instead of always calling or().
  4. If two OR groups are needed, issue two queries and combine results in code.

Example fix

// before
query.or().jobTenantId("t1");
query.or().handlerType("timer"); // throws: already in or statement

// after
query.or()
    .jobTenantId("t1")
    .handlerType("timer")
    .endOr();
Defensive patterns

Strategy: validation

Validate before calling

if (!queryInOrMode) {
    query.or();
    queryInOrMode = true;
} // always call endOr() before opening another or-block

Try / catch

try {
    query.or();
} catch (org.flowable.common.engine.api.FlowableException e) {
    if (e.getMessage().contains("already in an or statement")) {
        query.endOr();
        query.or();
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling or() twice without an intervening endOr(); building queries recursively where an inner builder also calls or(); adding a shared 'default filter' helper that calls or() onto a query already in or-mode.

Common situations: Composable query-builder frameworks stacking filter fragments each of which opens its own or(); misreading or() as re-entrant like SQL parentheses; tests reusing one query object across multiple filter helpers.

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