{"record":{"id":"5f05d2c021e25cb1","repo":"flowable/flowable-engine","slug":"endor-can-only-be-called-after-calling-or-5f05d2","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-job-service/src/main/java/org/flowable/job/service/impl/JobQueryImpl.java","lineNumber":578,"sourceCode":"    @Override\n    public JobQuery or() {\n        if (inOrStatement) {\n            throw new FlowableException(\"the query is already in an or statement\");\n        }\n        inOrStatement = true;\n        if (commandContext != null) {\n            currentOrQueryObject = new JobQueryImpl(commandContext, jobServiceConfiguration);\n        } else {\n            currentOrQueryObject = new JobQueryImpl(commandExecutor, jobServiceConfiguration);\n        }\n        orQueryObjects.add(currentOrQueryObject);\n        return this;\n    }\n\n    @Override\n    public JobQuery endOr() {\n        if (!inOrStatement) {\n            throw new FlowableException(\"endOr() can only be called after calling or()\");\n        }\n        inOrStatement = false;\n        currentOrQueryObject = null;\n        return this;\n    }\n\n    // sorting //////////////////////////////////////////\n\n    @Override\n    public JobQuery orderByJobDuedate() {\n        return orderBy(JobQueryProperty.DUEDATE);\n    }\n\n    @Override\n    public JobQuery orderByJobCreateTime() {\n        return orderBy(JobQueryProperty.CREATE_TIME);\n    }\n","sourceCodeStart":560,"sourceCodeEnd":596,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/JobQueryImpl.java#L560-L596","documentation":"Flowable query objects support an 'or(...)' block in which conditions are OR-ed instead of AND-ed. endOr() closes that block, but it is only valid inside one. The JobQueryImpl.endOr() method checks the internal inOrStatement flag and throws FlowableException when endOr() is called without a preceding or() call.","triggerScenarios":"Calling jobQuery.endOr() directly without calling jobQuery.or() first; calling endOr() twice after a single or(); calling endOr() on a fresh query instance with no or-state at all.","commonSituations":"Copy-pasting query-building code where the or() line was deleted or commented out; dynamically assembling queries where an endOr() call is emitted unconditionally; refactoring that removed or() but left endOr() in place; building queries in loops that append endOr() per iteration.","solutions":["Ensure every endOr() call is preceded by a matching or() call on the same query object","Wrap the endOr() call in a flag/condition that tracks whether or() was actually invoked","Remove the stray endOr() call if no OR conditions are needed"],"exampleFix":"// before\nJobQuery query = taskService.createJobQuery()\n    .processInstanceId(pid)\n    .endOr(); // throws: or() never called\n// after\nJobQuery query = taskService.createJobQuery()\n    .processInstanceId(pid)\n    .or()\n      .jobId(id1)\n      .jobId(id2)\n    .endOr();","handlingStrategy":"validation","validationCode":"// Guard at the call site\nboolean inOr = false;\nJobQuery q = managementService.createJobQuery();\n// only call endOr if a matching or() was issued\nif (inOr) { q.endOr(); }","typeGuard":"boolean canEndOr(JobQueryImpl q) { try { java.lang.reflect.Field f = JobQueryImpl.class.getDeclaredField(\"inOrStatement\"); f.setAccessible(true); return f.getBoolean(q); } catch (Exception e) { return false; } }","tryCatchPattern":"try {\n    query.endOr();\n} catch (FlowableException e) {\n    if (!e.getMessage().contains(\"endOr()\")) throw e;\n    // or() was not open: rebuild query without endOr\n}","preventionTips":["Always pair or()...endOr() in the same code block, never across branches","Use a query-builder helper that tracks or-state instead of raw chaining","Add unit tests covering both with-or and without-or query paths"],"tags":["flowable","query-builder","invalid-api-usage","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-18T11:17:12.947Z"}