{"record":{"id":"eaab76838ac2f6f7","repo":"flowable/flowable-engine","slug":"endor-can-only-be-called-after-calling-or-eaab76","errorCode":null,"errorMessage":"endOr() can only be called after calling or()","messagePattern":"endOr\\(\\) can only be called after calling or\\(\\)","errorType":"validation","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/ExecutionQueryImpl.java","lineNumber":996,"sourceCode":"    public ExecutionQuery 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 ExecutionQueryImpl(commandContext, processEngineConfiguration);\n        } else {\n            currentOrQueryObject = new ExecutionQueryImpl(commandExecutor, processEngineConfiguration);\n        }\n        orQueryObjects.add(currentOrQueryObject);\n        return this;\n    }\n\n    @Override\n    public ExecutionQuery 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    // ordering ////////////////////////////////////////////////////\n\n    @Override\n    public ExecutionQueryImpl orderByProcessInstanceId() {\n        this.orderProperty = ExecutionQueryProperty.PROCESS_INSTANCE_ID;\n        return this;\n    }\n\n    @Override\n    public ExecutionQueryImpl orderByProcessDefinitionId() {\n        this.orderProperty = ExecutionQueryProperty.PROCESS_DEFINITION_ID;","sourceCodeStart":978,"sourceCodeEnd":1014,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/ExecutionQueryImpl.java#L978-L1014","documentation":"API-misuse guard in ExecutionQueryImpl.endOr: endOr() was invoked while no OR block was open (or() was never called on this query), leaving the query's or-state unbalanced and the fluent chain invalid.","triggerScenarios":"Calling query.endOr() without ever calling or(); calling endOr() twice after a single or(); or copying fragments of builder code where the or() call is conditionally skipped but endOr() always runs.","commonSituations":"Query assembly code with conditional logic (if (a) or(); if (b) ...) where the or() branch was not taken; accidental duplicate endOr() in chained builders.","solutions":["Ensure endOr() is only invoked when or() was called earlier on the same query","Track open/close state in your builder wrapper before emitting endOr()","Remove stray endOr() calls when the query has no OR criteria"],"exampleFix":"// before\nquery.processDefinitionKey(\"k1\").endOr();\n\n// after\nquery.or().processDefinitionKey(\"k1\").endOr();","handlingStrategy":"validation","validationCode":"if (builder.isInsideOrBlock()) {\n    builder.closeOr();\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()\")) throw e;\n    // no OR block was open; proceed without closing\n}","preventionTips":["Pair every endOr() with a preceding or() in the same code path","Avoid conditional or() with unconditional endOr()","Use try/finally only around an already-opened OR block"],"tags":["flowable","query","or-statement","invalid-state","java"],"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-14T16:17:12.679Z"}