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
HistoryJobQuery.endOr() throws FlowableException when called while not inside an or() block (inOrStatement == false). endOr() closes an OR group started by or(); closing a group that was never opened is a caller bug, so the library rejects it immediately.
Solutions
- Only call endOr() after a matching or() call
- Remove the extra endOr() call
- Track or-state in builder helpers so endOr() is emitted only when a block is open
- Check that the or()/endOr() pair wraps exactly the intended criteria
Example fix
// before query.jobId(id).endOr(); // no or() opened // after query.or().jobId(id).endOr();
Defensive patterns
Strategy: validation
Validate before calling
// track in your builder:
// boolean inOr = false;
// if (inOr) { query.endOr(); inOr = false; } Try / catch
try {
builder.finishQuery(query);
} catch (FlowableException e) {
if (e.getMessage().contains("endOr() can only be called after calling or()")) {
throw new IllegalStateException("endOr() without matching or()", e);
}
throw e;
} Prevention
- Only emit endOr() when or() was previously emitted (track builder state)
- Avoid copy-pasted endOr() lines in generated code
- Keep or()/endOr() pairs lexically adjacent in the same method
When it happens
Trigger: Calling query.endOr() without a preceding or(), calling endOr() twice after one or(), or calling endOr() on a fresh query object in helper code that unconditionally closes the block.
Common situations: Template/generated query-building code that always appends endOr(); refactoring that removed the or() call but kept endOr(); duplicated endOr() lines from copy-paste.
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()
- endOr() can only be called after calling or()
- endOr() can only be called after calling or()
- endOr() can only be called after calling or()
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/6d018a7ede69e31e.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/HistoryJobQueryImpl.java:242
@Override
public HistoryJobQuery or() {
if (inOrStatement) {
throw new FlowableException("the query is already in an or statement");
}
inOrStatement = true;
if (commandContext != null) {
currentOrQueryObject = new HistoryJobQueryImpl(commandContext, jobServiceConfiguration);
} else {
currentOrQueryObject = new HistoryJobQueryImpl(commandExecutor, jobServiceConfiguration);
}
orQueryObjects.add(currentOrQueryObject);
return this;
}
@Override
public HistoryJobQuery endOr() {
if (!inOrStatement) {
throw new FlowableException("endOr() can only be called after calling or()");
}
inOrStatement = false;
currentOrQueryObject = null;
return this;
}
// sorting //////////////////////////////////////////
@Override
public HistoryJobQuery orderByJobId() {
return orderBy(JobQueryProperty.JOB_ID);
}
@Override
public HistoryJobQuery orderByJobRetries() {
return orderBy(JobQueryProperty.RETRIES);
}
View on GitHub (pinned to d6d39ce1c6)