flowable/flowable-engine · error · org.activiti.engine.ActivitiException

condition script returns null

Error message

condition script returns null: ${expression}

What it means

ScriptCondition.evaluate runs the condition expression through the ScriptingEngines for the configured language. If the script evaluates to null the condition cannot be interpreted as true/false, so an ActivitiException is thrown including the original expression text.

Solutions

  1. Ensure the condition script explicitly returns a boolean (add 'return' where the language requires it).
  2. Check the script's last statement actually produces a value rather than calling a void method.
  3. If using JavaScript, return explicit true/false instead of relying on truthy/falsy or undefined values.
  4. Verify the delegated bean/method used in the expression cannot return null.

Example fix

// before (Groovy condition)
${orderService.checkApproved(execution)} // void or null-returning
// after
${return orderService.isApproved(execution)}
Defensive patterns

Strategy: validation

Validate before calling

// audit condition scripts before deployment
if (!conditionScript.matches("(?s).*\\breturn\\b.*")) {
    throw new IllegalArgumentException("condition script must return a value: " + conditionScript);
}

Try / catch

try {
    conditionResult = ScriptCondition.evaluate(...);
} catch (ActivitiException e) {
    if (e.getMessage().contains("returns null")) {
        throw new InvalidConditionException("fix the script to return a boolean");
    }
    throw e;
}

Prevention

When it happens

Trigger: A sequence-flow condition script (e.g. a Groovy/JS condition on a flow) whose last statement evaluates to null or has no return value — commonly a script that performs an action but never returns a value.

Common situations: Groovy/JavaScript scripts that forget 'return', scripts whose final expression is a void method call, dynamic language returning undefined (JS) instead of a boolean, condition script delegating to a bean method returning null.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/458631eb788145d8. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/scripting/ScriptCondition.java:55

    }

    @Override
    public boolean evaluate(String sequenceFlowId, DelegateExecution execution) {
        String conditionExpression = null;
        if (Context.getProcessEngineConfiguration().isEnableProcessDefinitionInfoCache()) {
            ObjectNode elementProperties = Context.getBpmnOverrideElementProperties(sequenceFlowId, execution.getProcessDefinitionId());
            conditionExpression = getActiveValue(expression, DynamicBpmnConstants.SEQUENCE_FLOW_CONDITION, elementProperties);
        } else {
            conditionExpression = expression;
        }

        ScriptingEngines scriptingEngines = Context
                .getProcessEngineConfiguration()
                .getScriptingEngines();

        Object result = scriptingEngines.evaluate(conditionExpression, language, execution);
        if (result == null) {
            throw new ActivitiException("condition script returns null: " + expression);
        }
        if (!(result instanceof Boolean)) {
            throw new ActivitiException("condition script returns non-Boolean: " + result + " (" + result.getClass().getName() + ")");
        }
        return (Boolean) result;
    }

    protected String getActiveValue(String originalValue, String propertyName, ObjectNode elementProperties) {
        String activeValue = originalValue;
        if (elementProperties != null) {
            JsonNode overrideValueNode = elementProperties.get(propertyName);
            if (overrideValueNode != null) {
                if (overrideValueNode.isNull()) {
                    activeValue = null;
                } else {
                    activeValue = overrideValueNode.asString();
                }
            }

View on GitHub (pinned to d6d39ce1c6)