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
Flowable throws this FlowableException from HistoricProcessInstanceQuery.endOr() when it is called without a matching preceding or() call. endOr() only closes an open OR block; the inOrStatement flag must be true. This enforces balanced or()/endOr() pairs in query construction.
Source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/HistoricProcessInstanceQueryImpl.java:1132
public HistoricProcessInstanceQuery or() {
if (inOrStatement) {
throw new FlowableException("the query is already in an or statement");
}
inOrStatement = true;
if (commandContext != null) {
currentOrQueryObject = new HistoricProcessInstanceQueryImpl(commandContext, processEngineConfiguration);
} else {
currentOrQueryObject = new HistoricProcessInstanceQueryImpl(commandExecutor, processEngineConfiguration);
}
orQueryObjects.add(currentOrQueryObject);
return this;
}
@Override
public HistoricProcessInstanceQuery endOr() {
if (!inOrStatement) {
throw new FlowableException("endOr() can only be called after calling or()");
}
inOrStatement = false;
currentOrQueryObject = null;
return this;
}
@Override
public HistoricProcessInstanceQuery orderByProcessInstanceBusinessKey() {
return orderBy(HistoricProcessInstanceQueryProperty.BUSINESS_KEY);
}
@Override
public HistoricProcessInstanceQuery orderByProcessInstanceDuration() {
return orderBy(HistoricProcessInstanceQueryProperty.DURATION);
}
@OverrideView on GitHub (pinned to d6d39ce1c6)
Solutions
- Only call endOr() when a previous or() opened a block
- Track the or-block state in your builder and conditionally emit endOr()
- Remove redundant endOr() calls from query construction code
Example fix
// before
if (applyFilter) { query.endOr(); }
// after
if (orBlockOpen) {
query.endOr();
orBlockOpen = false;
} Defensive patterns
Strategy: validation
Validate before calling
boolean orBlockOpen = false;
void safeEndOr(HistoricProcessInstanceQuery q) {
if (orBlockOpen) { q.endOr(); orBlockOpen = false; }
} Type guard
boolean canEndOrBlock(boolean inOrStatement) {
return inOrStatement;
} Try / catch
try {
query.endOr();
} catch (FlowableException e) {
LOG.debug("No or() block open; ignoring endOr() call");
} Prevention
- Only emit endOr() when a prior or() opened a block
- Track the block state in custom query builders and close it exactly once
- Avoid copying or()/endOr() pairs unconditionally when filters are optional
When it happens
Trigger: Calling query.endOr() before any or(), or calling endOr() twice after a single or() (the second call finds inOrStatement already false).
Common situations: Query-builder code unconditionally appending endOr() even when no or-conditions were added; duplicated endOr() calls after refactoring.
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
- the query is already in an or statement
- the query is already in an or statement
- endOr() can only be called after calling or()
- the query is already in an or statement
- endOr() can only be called after calling or()
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/d27bfe4af7931f72.
Report an issue: GitHub.