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
EventSubscriptionQuery.endOr() closes an OR block opened by or(). Flowable throws FlowableException when endOr() is called without an active OR statement, keeping the or()/endOr() bracket strictly paired. The query object tracks this with the inOrStatement flag at EventSubscriptionQueryImpl.java:486.
Source
Thrown at modules/flowable-eventsubscription-service/src/main/java/org/flowable/eventsubscription/service/impl/EventSubscriptionQueryImpl.java:486
return this;
}
@Override
public EventSubscriptionQuery or() {
if (inOrStatement) {
throw new FlowableException("the query is already in an or statement");
}
inOrStatement = true;
currentOrQueryObject = new EventSubscriptionQueryImpl();
orQueryObjects.add(currentOrQueryObject);
return this;
}
@Override
public EventSubscriptionQuery endOr() {
if (!inOrStatement) {
throw new FlowableException("endOr() can only be called after calling or()");
}
inOrStatement = false;
currentOrQueryObject = null;
return this;
}
@Override
public EventSubscriptionQuery orderById() {
return orderBy(EventSubscriptionQueryProperty.ID);
}
@Override
public EventSubscriptionQuery orderByExecutionId() {
return orderBy(EventSubscriptionQueryProperty.EXECUTION_ID);
}
@OverrideView on GitHub (pinned to d6d39ce1c6)
Solutions
- Ensure endOr() is only called after a successful or() and exactly once per OR block
- Make or()/endOr() calls unconditional and adjacent, adding predicates between them
- Remove the stray endOr() call if no OR grouping is needed
Example fix
// before
if (useOr) {
query.or();
}
query.endOr();
// after
if (useOr) {
query.or();
query.eventSubscriptionType("message");
query.endOr();
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!orOpened) {
throw new IllegalStateException("endOr() called without or()");
} Try / catch
try {
query.endOr();
} catch (FlowableException e) {
log.error("endOr() without or(): {}", e.getMessage());
throw e;
} Prevention
- Track OR-block state with a local boolean when building queries dynamically
- Keep or()/endOr() paired in the same code block
- Remove endOr() calls after refactors that dropped or()
When it happens
Trigger: Calling query.endOr() with no preceding or(), calling endOr() twice, or calling endOr() after an early return/control-flow path already closed the block.
Common situations: Refactored query builders where the or() call was removed or made conditional but the matching endOr() remained; exception-handling paths that skip or() but still reach endOr() in a finally-style cleanup.
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
- endOr() can only be called after calling or()
- the query is already in an or statement
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/fa16615c72183105.
Report an issue: GitHub.