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
CaseInstanceQueryImpl.or() throws FlowableException when the query is already inside an or() block. Flowable supports only a single, flat OR block per query — nested or sequential or() calls without a matching endOr() are not supported.
Solutions
- Call endOr() before starting another or() block, or restructure so all OR conditions are added within a single or()...endOr() section.
- Remove the second or() call and add the extra conditions inside the existing OR block.
- In loop-based builders, call or() once before the loop and endOr() after it.
- Chain queries with FlowableOrQueryObject semantics only where the API allows; otherwise run multiple queries and merge results in application code.
Example fix
// before
query.or().caseDefinitionKey("A").or().caseDefinitionKey("B"); // second or() throws
// after
query.or().caseDefinitionKey("A").caseDefinitionKey("B").endOr(); Defensive patterns
Strategy: try-catch
Validate before calling
if (query instanceof CaseInstanceQueryImpl && ((CaseInstanceQueryImpl) query).inOrStatement) {
throw new IllegalStateException("or() already active; call endOr() first");
}
query.or(); Try / catch
try {
query.or();
} catch (FlowableException e) {
if (e.getMessage().contains("already in an or statement")) {
log.warn("Duplicate or() ignored");
} else {
throw e;
}
} Prevention
- Always pair or() with endOr() in the same builder method
- Never call or() inside loops; open one OR block around the loop body
- Centralize OR-block construction in a single helper to guarantee pairing
- Track OR state in your own builder wrapper
When it happens
Trigger: Calling query.or() twice without an intervening endOr(); building the query in a loop that calls or() per filter condition; combining multiple filter builders that each open an or() block on the same query object.
Common situations: Dynamic query builders accumulating 'any of these' conditions in a loop; copy-pasted filter code that opens or() at multiple levels; framework integrations layering or() blocks from different filter sources.
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
- endOr() can only be called after calling or()
- after time is null
- Cannot use taskIds together with excludeLocalVariables
- decision tenantId is null
- endOr() can only be called after calling or()
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/aa593ff00cb57e7c.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/CaseInstanceQueryImpl.java:873
public CaseInstanceQuery involvedGroups(Set<String> groupIds) {
if (groupIds == null) {
throw new FlowableIllegalArgumentException("involvedGroups are null");
}
if (groupIds.isEmpty()) {
throw new FlowableIllegalArgumentException("involvedGroups are empty");
}
if (inOrStatement) {
this.currentOrQueryObject.involvedGroups = groupIds;
} else {
this.involvedGroups = groupIds;
}
return this;
}
@Override
public CaseInstanceQuery or() {
if (inOrStatement) {
throw new FlowableException("the query is already in an or statement");
}
inOrStatement = true;
if (commandContext != null) {
currentOrQueryObject = new CaseInstanceQueryImpl(commandContext, cmmnEngineConfiguration);
} else {
currentOrQueryObject = new CaseInstanceQueryImpl(commandExecutor, cmmnEngineConfiguration);
}
orQueryObjects.add(currentOrQueryObject);
return this;
}
@Override
public CaseInstanceQuery endOr() {
if (!inOrStatement) {
throw new FlowableException("endOr() can only be called after calling or()");
}
View on GitHub (pinned to d6d39ce1c6)