{"record":{"id":"9debefa866f6ac0a","repo":"flowable/flowable-engine","slug":"variable-operation-is-missing-for-variable-va","errorCode":null,"errorMessage":"Variable operation is missing for variable: \" + variable.getName()","messagePattern":"Variable operation is missing for variable: \" \\+ variable\\.getName\\(\\)","errorType":"validation","errorClass":"FlowableIllegalArgumentException","httpStatus":400,"severity":"error","filePath":"modules/flowable-rest/src/main/java/org/flowable/rest/service/api/history/HistoricProcessInstanceBaseResource.java","lineNumber":313,"sourceCode":"            restApiInterceptor.accessHistoryProcessInfoById(processInstance);\n        }\n        \n        return processInstance;\n    }\n\n    protected HistoricProcessInstance getHistoricProcessInstanceFromRequestWithoutAccessCheck(String processInstanceId) {\n        HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();\n        if (processInstance == null) {\n            throw new FlowableObjectNotFoundException(\"Could not find a process instance with id '\" + processInstanceId + \"'.\", HistoricProcessInstance.class);\n        }\n\n        return processInstance;\n    }\n\n    protected void addVariables(HistoricProcessInstanceQuery processInstanceQuery, List<QueryVariable> variables) {\n        for (QueryVariable variable : variables) {\n            if (variable.getVariableOperation() == null) {\n                throw new FlowableIllegalArgumentException(\"Variable operation is missing for variable: \" + variable.getName());\n            }\n            if (variable.getVariableOperation() != QueryVariableOperation.EXISTS && variable.getVariableOperation() != QueryVariableOperation.NOT_EXISTS) {\n                if (variable.getValue() == null) {\n                    throw new FlowableIllegalArgumentException(\"Variable value is missing for variable: \" + variable.getName());\n                }\n            }\n\n            boolean nameLess = variable.getName() == null;\n\n            Object actualValue = restResponseFactory.getVariableValue(variable);\n\n            // A value-only query is only possible using equals-operator\n            if (nameLess && variable.getVariableOperation() != QueryVariableOperation.EQUALS) {\n                throw new FlowableIllegalArgumentException(\"Value-only query (without a variable-name) is only supported when using 'equals' operation.\");\n            }\n\n            switch (variable.getVariableOperation()) {\n","sourceCodeStart":295,"sourceCodeEnd":331,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-rest/src/main/java/org/flowable/rest/service/api/history/HistoricProcessInstanceBaseResource.java#L295-L331","documentation":"When building a historic process instance query from REST query parameters, each variable filter must declare an operation (equals, like, gt, exists, ...). addVariables throws FlowableIllegalArgumentException if a QueryVariable has no variableOperation, because the query cannot know how to compare the value.","triggerScenarios":"GET /history/historic-process-instances with a variable query parameter (variable=..., scope) whose operation part is missing or misspelled, e.g. 'variable=name|value' without the operator segment.","commonSituations":"Hand-built query strings missing the operator segment; client library sending variable filters without mapping the operation; typo in operator name leading to parse failure.","solutions":["Use the correct variable query syntax including the operation, e.g. variable=name|eq|value","Check supported QueryVariableOperation names and use exactly those (equals, notEquals, gt, gte, lt, lte, like, exists, notExists...)","Catch FlowableIllegalArgumentException (HTTP 400) and validate variable filters client-side before issuing the query"],"exampleFix":"// before\nGET /history/historic-process-instances?variable=orderAmount|2500\n\n// after\nGET /history/historic-process-instances?variable=orderAmount|equals|2500","handlingStrategy":"validation","validationCode":"const OPS = ['equals','notEquals','gt','gte','lt','lte','like','exists','notExists'];\nfor (const v of variables) {\n  if (!v.operation || !OPS.includes(v.operation)) throw new Error(`Variable ${v.name}: missing/invalid operation`);\n}","typeGuard":"function hasOperation(v) { return v != null && typeof v.operation === 'string' && v.operation.length > 0; }","tryCatchPattern":"try { return await queryHistoricInstances(vars); } catch (e) { if (e.status === 400) showFilterSyntaxHelp(e); else throw e; }","preventionTips":["Use the documented variable=name|op|value query syntax","Centralize variable-filter construction in one helper","Validate operator names against QueryVariableOperation enum values"],"tags":["rest-api","flowable","query-parameter","validation"],"backgroundTag":"invalid-query-parameter","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"}