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
endOr() closes an OR block previously opened with or() on a TimerJobQuery. If endOr() is called without a preceding or(), the query is not in OR mode (inOrStatement is false), so the library throws this FlowableException rather than silently ignoring the call.
Source
Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/TimerJobQueryImpl.java:554
@Override
public TimerJobQuery or() {
if (inOrStatement) {
throw new FlowableException("the query is already in an or statement");
}
inOrStatement = true;
if (commandContext != null) {
currentOrQueryObject = new TimerJobQueryImpl(commandContext, jobServiceConfiguration);
} else {
currentOrQueryObject = new TimerJobQueryImpl(commandExecutor, jobServiceConfiguration);
}
orQueryObjects.add(currentOrQueryObject);
return this;
}
@Override
public TimerJobQuery endOr() {
if (!inOrStatement) {
throw new FlowableException("endOr() can only be called after calling or()");
}
inOrStatement = false;
currentOrQueryObject = null;
return this;
}
// sorting //////////////////////////////////////////
@Override
public TimerJobQuery orderByJobDuedate() {
return orderBy(JobQueryProperty.DUEDATE);
}
@Override
public TimerJobQuery orderByJobCreateTime() {
return orderBy(JobQueryProperty.CREATE_TIME);
}
View on GitHub (pinned to d6d39ce1c6)
Solutions
- Remove the stray endOr() call or add the matching or() before it
- Ensure every endOr() in your query-building helper is paired one-to-one with or()
- Assert the query is inside an OR block before emitting endOr() in generated/builder code
Example fix
// before
query.jobId("1").endOr().list(); // no or() opened
// after
query.or().jobId("1").endOr().list(); Defensive patterns
Strategy: validation
Validate before calling
if (query instanceof TimerJobQueryImpl && !((TimerJobQueryImpl) query).isInOrStatement()) { throw new IllegalStateException("endOr() without or()"); } Type guard
boolean canCallEndOr(TimerJobQueryImpl q) { return q != null && q.isInOrStatement(); } Prevention
- Only emit endOr() after a preceding or() call
- Track OR-block state in wrapper builders
- Test generated queries for balanced or/endOr
When it happens
Trigger: Calling endOr() on a freshly created TimerJobQuery, or calling endOr() twice (a second endOr() after the block was already closed).
Common situations: Unbalanced query-builder code where endOr() is emitted unconditionally by a template/helper; refactoring that removed the or() call but kept endOr(); misreading the API and thinking endOr() starts rather than closes an OR section.
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()
- Cannot combine onlyInbound() with onlyOutbound() in the same
- Cannot combine onlyOutbound() with onlyInbound() in the same
- the query is already in an or statement
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/007501442d71a30d.
Report an issue: GitHub.