{"record":{"id":"bc2c65a00375b8cd","repo":"flowable/flowable-engine","slug":"unable-to-resolve-formfieldvalidationexpression-to","errorCode":null,"errorMessage":"Unable to resolve formFieldValidationExpression to boolean value for ${variableContainer}","messagePattern":"Unable to resolve formFieldValidationExpression to boolean value for (.+?)","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/task/TaskHelper.java","lineNumber":271,"sourceCode":"            taskServiceConfiguration.getInternalHistoryTaskManager().recordHistoryUserTaskLog(taskLogEntryBuilder);\n        }\n    }\n\n    public static boolean isFormFieldValidationEnabled(VariableContainer variableContainer,\n        CmmnEngineConfiguration cmmnEngineConfiguration, String formFieldValidationExpression) {\n        if (StringUtils.isNotEmpty(formFieldValidationExpression)) {\n            Boolean formFieldValidation = getBoolean(formFieldValidationExpression);\n            if (formFieldValidation != null) {\n                return formFieldValidation;\n            }\n\n            if (variableContainer != null) {\n                ExpressionManager expressionManager = cmmnEngineConfiguration.getExpressionManager();\n                Boolean formFieldValidationValue = getBoolean(\n                    expressionManager.createExpression(formFieldValidationExpression).getValue(variableContainer)\n                );\n                if (formFieldValidationValue == null) {\n                    throw new FlowableException(\"Unable to resolve formFieldValidationExpression to boolean value for \" + variableContainer);\n                }\n                return formFieldValidationValue;\n            }\n            throw new FlowableException(\"Unable to resolve formFieldValidationExpression without variable container\");\n        }\n        return true;\n    }\n    \n    protected static void bulkDeleteHistoricTaskInstances(Collection<String> taskIds, CmmnEngineConfiguration cmmnEngineConfiguration) {\n        HistoricTaskService historicTaskService = cmmnEngineConfiguration.getTaskServiceConfiguration().getHistoricTaskService();\n        \n        List<String> subTaskIds = historicTaskService.findHistoricTaskIdsByParentTaskIds(taskIds);\n        if (subTaskIds != null && !subTaskIds.isEmpty()) {\n            bulkDeleteHistoricTaskInstances(subTaskIds, cmmnEngineConfiguration);\n        }\n        \n        cmmnEngineConfiguration.getVariableServiceConfiguration().getHistoricVariableService().bulkDeleteHistoricVariableInstancesByTaskIds(taskIds);\n        cmmnEngineConfiguration.getIdentityLinkServiceConfiguration().getHistoricIdentityLinkService().bulkDeleteHistoricIdentityLinksForTaskIds(taskIds);","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/task/TaskHelper.java#L253-L289","documentation":"TaskHelper.isFormFieldValidationEnabled evaluates the formFieldValidationExpression via the expression manager and requires the result to be a boolean (per getBoolean coercion). If the expression evaluates to something that cannot be interpreted as a Boolean (getBoolean returns null, e.g. a number or arbitrary object), this FlowableException is thrown because the engine cannot decide whether form field validation is on.","triggerScenarios":"A CMMN task/case definition sets cmmn:formFieldValidationExpression to an expression returning a non-boolean (e.g. a string other than 'true'/'false' handled by a stricter path, a number, or null result) while a variableContainer is present; the task then renders/validates its form.","commonSituations":"Typo in the expression returning e.g. 'yes'/'1' or an integer; expression referencing a missing variable yielding null; copying a validation expression pattern from a place where strings are coerced differently.","solutions":["Fix the expression so it evaluates to a real boolean, e.g. ${userInputRequired == true} or ${someFlag}, not ${someFlagValue} returning a String/number","Ensure all variables referenced by the expression are set on the variableContainer before the task form is processed","Test the expression with a known variable set (e.g. via a small unit test using FlowableExpressionTestUtil or equivalent) to confirm it yields Boolean true/false","If the value can legitimately be non-boolean, sanitize it beforehand and pass a boolean variable to the expression"],"exampleFix":"// before\ncmmn:formFieldValidationExpression=\"${validationLevel}\"\n// after\ncmmn:formFieldValidationExpression=\"${validationLevel > 0}\"","handlingStrategy":"validation","validationCode":"Object v = expressionValue(formFieldValidationExpression, variableContainer);\nif (!(v instanceof Boolean) && !(\"true\".equalsIgnoreCase(String.valueOf(v)) || \"false\".equalsIgnoreCase(String.valueOf(v)))) {\n    throw new IllegalStateException(\"formFieldValidationExpression must be boolean, got: \" + v);\n}","typeGuard":"boolean isBooleanLike(Object v) { return v instanceof Boolean || \"true\".equalsIgnoreCase(String.valueOf(v)) || \"false\".equalsIgnoreCase(String.valueOf(v)); }","tryCatchPattern":"try { enabled = TaskHelper.isFormFieldValidationEnabled(cfg, expr, container); } catch (FlowableException e) { if (e.getMessage().startsWith(\"Unable to resolve formFieldValidationExpression\")) { log.error(\"Non-boolean validation expression: {}\", expr); enabled = true; } else throw e; }","preventionTips":["Write validation expressions as explicit comparisons (x == true)","Ensure referenced variables are initialized before form processing","Unit-test expressions against expected variable types"],"tags":["cmmn","expression","form-validation","flowable"],"backgroundTag":"type-mismatch","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}