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 when HistoricTaskInstanceQuery.endOr() is called while no or-block is open (or() was never called, or endOr() was already called once). endOr() only closes an existing or-group, so calling it standalone is invalid query usage. It fails fast at query-construction time.
Source
Thrown at modules/flowable-task-service/src/main/java/org/flowable/task/service/impl/HistoricTaskInstanceQueryImpl.java:2082
public HistoricTaskInstanceQuery or() {
if (inOrStatement) {
throw new FlowableException("the query is already in an or statement");
}
inOrStatement = true;
if (databaseType != null) {
currentOrQueryObject = new HistoricTaskInstanceQueryImpl(commandExecutor, databaseType, taskServiceConfiguration, variableServiceConfiguration);
} else {
currentOrQueryObject = new HistoricTaskInstanceQueryImpl(commandExecutor, taskServiceConfiguration, variableServiceConfiguration);
}
orQueryObjects.add(currentOrQueryObject);
return this;
}
@Override
public HistoricTaskInstanceQuery endOr() {
if (!inOrStatement) {
throw new FlowableException("endOr() can only be called after calling or()");
}
inOrStatement = false;
currentOrQueryObject = null;
return this;
}
// ordering
// /////////////////////////////////////////////////////////////////
@Override
public HistoricTaskInstanceQueryImpl orderByTaskId() {
orderBy(HistoricTaskInstanceQueryProperty.HISTORIC_TASK_INSTANCE_ID);
return this;
}
@Override
public HistoricTaskInstanceQueryImpl orderByHistoricActivityInstanceId() {View on GitHub (pinned to d6d39ce1c6)
Solutions
- Only call endOr() when a matching or() was called earlier in the chain.
- Make or()/endOr() calls paired in your builder code (e.g. always emit both together or neither).
- If no or-group is needed, remove the endOr() call entirely.
- Log/inspect the full query chain to find the unbalanced or()/endOr() pair.
Example fix
// before
query.taskAssignee("john").endOr(); // throws
// after
query.taskAssignee("john"); // no or-block, no endOr() Defensive patterns
Strategy: validation
Validate before calling
boolean useOr = needOrGroup();
if (useOr) {
query.or();
}
// ... filters ...
if (useOr) {
query.endOr();
} Try / catch
try {
query.endOr();
} catch (FlowableException e) {
if (e.getMessage().startsWith("endOr() can only be called")) {
throw new IllegalStateException("Query builder misuse: endOr() without matching or()", e);
}
throw e;
} Prevention
- Track or-block state in your builder helper and emit endOr() only when a block is open
- Never append endOr() unconditionally in dynamic query assembly
- Keep or()/endOr() calls adjacent so mismatches are visible in code review
- Add a test that executes every assembled query to surface imbalance
When it happens
Trigger: Calling query.endOr() without a preceding query.or(), or calling endOr() twice after a single or().
Common situations: Dynamic query builders that unconditionally append endOr(); refactoring that removed the or() call but left endOr(); method chains assembled from optional fragments where the fragment containing or() was skipped.
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
- endOr() can only be called after calling or()
- the query is already in an or statement
- 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/745deef74b3a8290.
Report an issue: GitHub.