{"record":{"id":"fa16615c72183105","repo":"flowable/flowable-engine","slug":"endor-can-only-be-called-after-calling-or-fa1661","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-eventsubscription-service/src/main/java/org/flowable/eventsubscription/service/impl/EventSubscriptionQueryImpl.java","lineNumber":486,"sourceCode":"        return this;\n    }\n\n    @Override\n    public EventSubscriptionQuery or() {\n        if (inOrStatement) {\n            throw new FlowableException(\"the query is already in an or statement\");\n        }\n\n        inOrStatement = true;\n        currentOrQueryObject = new EventSubscriptionQueryImpl();\n        orQueryObjects.add(currentOrQueryObject);\n        return this;\n    }\n\n    @Override\n    public EventSubscriptionQuery 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 EventSubscriptionQuery orderById() {\n        return orderBy(EventSubscriptionQueryProperty.ID);\n    }\n\n    @Override\n    public EventSubscriptionQuery orderByExecutionId() {\n        return orderBy(EventSubscriptionQueryProperty.EXECUTION_ID);\n    }\n\n    @Override","sourceCodeStart":468,"sourceCodeEnd":504,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-eventsubscription-service/src/main/java/org/flowable/eventsubscription/service/impl/EventSubscriptionQueryImpl.java#L468-L504","documentation":"EventSubscriptionQuery.endOr() closes an OR block opened by or(). Flowable throws FlowableException when endOr() is called without an active OR statement, keeping the or()/endOr() bracket strictly paired. The query object tracks this with the inOrStatement flag at EventSubscriptionQueryImpl.java:486.","triggerScenarios":"Calling query.endOr() with no preceding or(), calling endOr() twice, or calling endOr() after an early return/control-flow path already closed the block.","commonSituations":"Refactored query builders where the or() call was removed or made conditional but the matching endOr() remained; exception-handling paths that skip or() but still reach endOr() in a finally-style cleanup.","solutions":["Ensure endOr() is only called after a successful or() and exactly once per OR block","Make or()/endOr() calls unconditional and adjacent, adding predicates between them","Remove the stray endOr() call if no OR grouping is needed"],"exampleFix":"// before\nif (useOr) {\n    query.or();\n}\nquery.endOr();\n\n// after\nif (useOr) {\n    query.or();\n    query.eventSubscriptionType(\"message\");\n    query.endOr();\n}","handlingStrategy":"try-catch","validationCode":"if (!orOpened) {\n    throw new IllegalStateException(\"endOr() called without or()\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    query.endOr();\n} catch (FlowableException e) {\n    log.error(\"endOr() without or(): {}\", e.getMessage());\n    throw e;\n}","preventionTips":["Track OR-block state with a local boolean when building queries dynamically","Keep or()/endOr() paired in the same code block","Remove endOr() calls after refactors that dropped or()"],"tags":["flowable","query","or-statement","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-14T11:17:12.474Z"}