{"record":{"id":"aa593ff00cb57e7c","repo":"flowable/flowable-engine","slug":"the-query-is-already-in-an-or-statement-aa593f","errorCode":null,"errorMessage":"the query is already in an or statement","messagePattern":"the query is already in an or statement","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/CaseInstanceQueryImpl.java","lineNumber":873,"sourceCode":"    public CaseInstanceQuery involvedGroups(Set<String> groupIds) {\n        if (groupIds == null) {\n            throw new FlowableIllegalArgumentException(\"involvedGroups are null\");\n        }\n        if (groupIds.isEmpty()) {\n            throw new FlowableIllegalArgumentException(\"involvedGroups are empty\");\n        }\n        if (inOrStatement) {\n            this.currentOrQueryObject.involvedGroups = groupIds;\n        } else {\n            this.involvedGroups = groupIds;\n        }\n        return this;\n    }\n\n    @Override\n    public CaseInstanceQuery or() {\n        if (inOrStatement) {\n            throw new FlowableException(\"the query is already in an or statement\");\n        }\n\n        inOrStatement = true;\n        if (commandContext != null) {\n            currentOrQueryObject = new CaseInstanceQueryImpl(commandContext, cmmnEngineConfiguration);\n        } else {\n            currentOrQueryObject = new CaseInstanceQueryImpl(commandExecutor, cmmnEngineConfiguration);\n        }\n        orQueryObjects.add(currentOrQueryObject);\n        return this;\n    }\n\n    @Override\n    public CaseInstanceQuery endOr() {\n        if (!inOrStatement) {\n            throw new FlowableException(\"endOr() can only be called after calling or()\");\n        }\n","sourceCodeStart":855,"sourceCodeEnd":891,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/CaseInstanceQueryImpl.java#L855-L891","documentation":"CaseInstanceQueryImpl.or() throws FlowableException when the query is already inside an or() block. Flowable supports only a single, flat OR block per query — nested or sequential or() calls without a matching endOr() are not supported.","triggerScenarios":"Calling query.or() twice without an intervening endOr(); building the query in a loop that calls or() per filter condition; combining multiple filter builders that each open an or() block on the same query object.","commonSituations":"Dynamic query builders accumulating 'any of these' conditions in a loop; copy-pasted filter code that opens or() at multiple levels; framework integrations layering or() blocks from different filter sources.","solutions":["Call endOr() before starting another or() block, or restructure so all OR conditions are added within a single or()...endOr() section.","Remove the second or() call and add the extra conditions inside the existing OR block.","In loop-based builders, call or() once before the loop and endOr() after it.","Chain queries with FlowableOrQueryObject semantics only where the API allows; otherwise run multiple queries and merge results in application code."],"exampleFix":"// before\nquery.or().caseDefinitionKey(\"A\").or().caseDefinitionKey(\"B\"); // second or() throws\n// after\nquery.or().caseDefinitionKey(\"A\").caseDefinitionKey(\"B\").endOr();","handlingStrategy":"try-catch","validationCode":"if (query instanceof CaseInstanceQueryImpl && ((CaseInstanceQueryImpl) query).inOrStatement) {\n    throw new IllegalStateException(\"or() already active; call endOr() first\");\n}\nquery.or();","typeGuard":null,"tryCatchPattern":"try {\n    query.or();\n} catch (FlowableException e) {\n    if (e.getMessage().contains(\"already in an or statement\")) {\n        log.warn(\"Duplicate or() ignored\");\n    } else {\n        throw e;\n    }\n}","preventionTips":["Always pair or() with endOr() in the same builder method","Never call or() inside loops; open one OR block around the loop body","Centralize OR-block construction in a single helper to guarantee pairing","Track OR state in your own builder wrapper"],"tags":["java","flowable","query-builder","or-query","api-misuse"],"backgroundTag":"invalid-state-transition","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T16:30:33.424Z"}