flowable/flowable-engine · error · FlowableException
the query is already in an or statement
Error message
the query is already in an or statement
What it means
Flowable's JobQuery API supports composing criteria with or(), which switches the query into OR mode and creates a nested or-query object. Calling or() twice without an intervening endOr() is an invalid state, so the library throws this FlowableException to protect the query builder's internal inOrStatement invariant.
Source
Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/TimerJobQueryImpl.java:539
this.tenantIdLike = tenantIdLike;
}
return this;
}
@Override
public TimerJobQueryImpl jobWithoutTenantId() {
if (inOrStatement) {
this.currentOrQueryObject.withoutTenantId = true;
} else {
this.withoutTenantId = true;
}
return this;
}
@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;View on GitHub (pinned to d6d39ce1c6)
Solutions
- Call endOr() after each or() block before starting a new or() block
- Refactor query building code so or()/endOr() pairs are always balanced (wrap in a helper that closes the block)
- Track inOrStatement-like state in your builder wrapper to assert balanced or/endOr calls
Example fix
// before
query.or().jobId("1").or().jobId("2").endOr().list();
// after
query.or().jobId("1").endOr().or().jobId("2").endOr().list(); Defensive patterns
Strategy: validation
Validate before calling
if (query instanceof TimerJobQueryImpl && ((TimerJobQueryImpl) query).isInOrStatement()) { throw new IllegalStateException("or() called twice: call endOr() first"); } Type guard
boolean canCallOr(TimerJobQueryImpl q) { return q != null && !q.isInOrStatement(); } Prevention
- Always pair or() with endOr() in the same block/scope
- Write query-builder helpers that emit balanced or/endOr pairs
- Add unit tests for dynamic query construction covering nested OR blocks
When it happens
Trigger: Calling timerJobQuery.createTimerJobQuery().or() ... or() again before calling endOr(); e.g. chaining .or().jobId('a').or() instead of closing the first OR block with .endOr().
Common situations: Building dynamic queries programmatically where an OR block is appended per filter and a code path forgets endOr(); copy-pasted query builders nesting or() blocks; upgrading code that assumed implicit re-entry into OR mode.
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
- endOr() can only be called after calling or()
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/e30f65c3df103cc3.
Report an issue: GitHub.