{"record":{"id":"2ad6302f883c8852","repo":"flowable/flowable-engine","slug":"condition-script-returns-non-boolean-result","errorCode":null,"errorMessage":"condition script returns non-Boolean: ${result} (${result.getClass().getName()})","messagePattern":"condition script returns non-Boolean: (.+?) \\((.+?)\\)","errorType":"exception","errorClass":"org.activiti.engine.ActivitiException","httpStatus":null,"severity":"error","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/scripting/ScriptCondition.java","lineNumber":58,"sourceCode":"    public boolean evaluate(String sequenceFlowId, DelegateExecution execution) {\n        String conditionExpression = null;\n        if (Context.getProcessEngineConfiguration().isEnableProcessDefinitionInfoCache()) {\n            ObjectNode elementProperties = Context.getBpmnOverrideElementProperties(sequenceFlowId, execution.getProcessDefinitionId());\n            conditionExpression = getActiveValue(expression, DynamicBpmnConstants.SEQUENCE_FLOW_CONDITION, elementProperties);\n        } else {\n            conditionExpression = expression;\n        }\n\n        ScriptingEngines scriptingEngines = Context\n                .getProcessEngineConfiguration()\n                .getScriptingEngines();\n\n        Object result = scriptingEngines.evaluate(conditionExpression, language, execution);\n        if (result == null) {\n            throw new ActivitiException(\"condition script returns null: \" + expression);\n        }\n        if (!(result instanceof Boolean)) {\n            throw new ActivitiException(\"condition script returns non-Boolean: \" + result + \" (\" + result.getClass().getName() + \")\");\n        }\n        return (Boolean) result;\n    }\n\n    protected String getActiveValue(String originalValue, String propertyName, ObjectNode elementProperties) {\n        String activeValue = originalValue;\n        if (elementProperties != null) {\n            JsonNode overrideValueNode = elementProperties.get(propertyName);\n            if (overrideValueNode != null) {\n                if (overrideValueNode.isNull()) {\n                    activeValue = null;\n                } else {\n                    activeValue = overrideValueNode.asString();\n                }\n            }\n        }\n        return activeValue;\n    }","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/scripting/ScriptCondition.java#L40-L76","documentation":"ScriptCondition.evaluate requires the condition script result to be a Boolean. When the script returns a non-Boolean value (String, Number, etc.) it throws ActivitiException including the result and its class name, because sequence-flow conditions must resolve to true/false.","triggerScenarios":"A condition script whose evaluation result is not a Boolean instance — e.g. returning a String \"true\", an Integer flag, or a truthy object from the scripting engine.","commonSituations":"Scripts returning string comparisons reversed ('true' vs true), JavaScript returning numbers/strings where Java expects Boolean, Groovy returning Integer result of a comparison wrapper, cross-version behavior where a different script engine returns boxed types differently.","solutions":["Make the script return an actual boolean (use comparison operators, not string/number results).","Convert explicitly: e.g. Boolean.parseBoolean(...) inside the script before returning.","Check which script engine/language is configured — different engines box results differently; normalize in the script.","For JavaScript, write 'return value === true;' instead of returning a truthy expression."],"exampleFix":"// before (JS condition)\nreturn approved; // approved is 1 or \"yes\"\n// after\nreturn approved === true || approved === \"yes\";","handlingStrategy":"validation","validationCode":"Object r = scriptingEngines.evaluate(expr, language, execution);\nif (!(r instanceof Boolean)) {\n    throw new IllegalStateException(\"condition must return Boolean, got \" + (r == null ? \"null\" : r.getClass()));\n}","typeGuard":"boolean isBooleanResult(Object r) {\n    return r instanceof Boolean;\n}","tryCatchPattern":"try {\n    return ScriptCondition.evaluate(...);\n} catch (ActivitiException e) {\n    if (e.getMessage().startsWith(\"condition script returns non-Boolean\")) {\n        throw new InvalidConditionException(e.getMessage());\n    }\n    throw e;\n}","preventionTips":["Return strict booleans from condition scripts; normalize strings/numbers inside the script.","Use === comparisons in JavaScript conditions, not truthiness.","Pin the script language attribute so results box consistently."],"tags":["scripting","condition","type-mismatch","bpmn"],"backgroundTag":"type-mismatch","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"}