{"record":{"id":"b1b99e5fb035ec35","repo":"flowable/flowable-engine","slug":"endor-can-only-be-called-after-calling-or-b1b99e","errorCode":null,"errorMessage":"endOr() can only be called after calling or()","messagePattern":"endOr\\(\\) can only be called after calling or\\(\\)","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":889,"sourceCode":"    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\n        inOrStatement = false;\n        currentOrQueryObject = null;\n        return this;\n    }\n    \n    @Override\n    public CaseInstanceQuery variableValueEquals(String variableName, Object variableValue) {\n        if (inOrStatement) {\n            currentOrQueryObject.variableValueEquals(variableName, variableValue, false);\n            return this;\n        } else {\n            return variableValueEquals(variableName, variableValue, false);\n        }\n    }\n\n    @Override","sourceCodeStart":871,"sourceCodeEnd":907,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/CaseInstanceQueryImpl.java#L871-L907","documentation":"API-misuse guard in CaseInstanceQueryImpl.endOr: the OR block was terminated without a matching or() call (or the internal inOrStatement flag was never set), so the query state is unbalanced and the builder refuses to continue.","triggerScenarios":"Calling query.endOr() unconditionally (e.g. in a finally block or generic builder teardown) when or() was never invoked; conditional code paths where or() was skipped but endOr() still runs; calling endOr() twice.","commonSituations":"Template/generic query builders that always emit endOr(); refactors where the or() call was removed but endOr() remained; accidental duplicate endOr() in chained builder code.","solutions":["Track whether or() was called (boolean flag) and only call endOr() conditionally.","Remove the stray endOr() call, or pair it with an or() that adds the intended OR conditions.","Audit chained builder code so every endOr() has exactly one matching or() and blocks are not nested.","Centralize OR-block construction in one helper method that owns both or() and endOr()."],"exampleFix":"// before\nquery.caseDefinitionKey(\"A\");\nquery.endOr(); // throws: or() never called\n// after\nboolean inOr = false;\nif (needsOrFilter) {\n    query.or().caseDefinitionKey(\"A\").caseDefinitionKey(\"B\");\n    inOr = true;\n}\nif (inOr) {\n    query.endOr();\n}","handlingStrategy":"try-catch","validationCode":"boolean orActive = query instanceof CaseInstanceQueryImpl && ((CaseInstanceQueryImpl) query).inOrStatement;\nif (orActive) {\n    query.endOr();\n}","typeGuard":null,"tryCatchPattern":"try {\n    query.endOr();\n} catch (FlowableException e) {\n    if (e.getMessage().contains(\"endOr() can only be called after calling or()\")) {\n        log.debug(\"endOr() without or(): ignored\");\n    } else {\n        throw e;\n    }\n}","preventionTips":["Use a boolean flag to remember whether or() was called before invoking endOr()","Never put endOr() in finally blocks or unconditional teardown code","Build OR sections inside dedicated helper methods that own both calls","Avoid calling endOr() more than once per or()"],"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-18T11:17:12.947Z"}