flowable/flowable-engine · error · FlowableIllegalArgumentException

Booleans and null cannot be used in 'less than' condition

Error message

Booleans and null cannot be used in 'less than' condition

What it means

Flowable variable queries reject null and Boolean values for the LESS_THAN operator since neither has a defined ordering in variable SQL comparisons. The guard is in AbstractVariableQueryImpl.addVariable and throws FlowableIllegalArgumentException immediately when the query clause is added. This is fail-fast API validation, not a runtime/database failure.

Solutions

  1. Replace LESS_THAN with EQUALS/NOT_EQUALS or EXISTS/NOT_EXISTS for null/boolean values
  2. Re-model the variable as a numeric or date type to allow ordering
  3. Validate the value before building the clause and skip or translate the condition
  4. Apply the comparison in application code after a broader query

Example fix

// before
query.variableValueLessThan("completed", null);
// after
query.variableValueEquals("completed", false); // or variableValueExists("completed")
Defensive patterns

Strategy: validation

Validate before calling

if (value == null || value instanceof Boolean) {
    throw new IllegalArgumentException("Use equals/exists operators for null or boolean variable values");
}
query.variableValueLessThan(name, value);

Type guard

static boolean supportsOrdering(Object v) {
    return v instanceof Number || v instanceof Date || v instanceof String;
}

Try / catch

try {
    query.variableValueLessThan(name, value);
} catch (org.flowable.common.engine.api.FlowableIllegalArgumentException e) {
    // degrade to equals or drop the clause
}

Prevention

When it happens

Trigger: Calling variableValueLessThan-style setters (routed through addVariable) with a null value or a Boolean, e.g. processVariableValueLessThan("enabled", false).

Common situations: Using a boolean flag as if it were comparable; nulls flowing from optional config into query builders; generic query-building code that forwards any Object as a comparison value.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/AbstractVariableQueryImpl.java:309

        addVariable(name, null, QueryOperator.NOT_EXISTS, scopeType, false);
        return (T) this;
    }

    protected void addVariable(String name, Object value, QueryOperator operator, boolean localScope) {
        this.addVariable(name, value, operator, null, localScope);
    }

    protected void addVariable(String name, Object value, QueryOperator operator, String scopeType, boolean localScope) {
        if (name == null) {
            throw new FlowableIllegalArgumentException("name is null");
        }
        if (value == null || isBoolean(value)) {
            // Null-values and booleans can only be used in EQUALS, NOT_EQUALS, EXISTS and NOT_EXISTS
            switch (operator) {
                case GREATER_THAN:
                    throw new FlowableIllegalArgumentException("Booleans and null cannot be used in 'greater than' condition");
                case LESS_THAN:
                    throw new FlowableIllegalArgumentException("Booleans and null cannot be used in 'less than' condition");
                case GREATER_THAN_OR_EQUAL:
                    throw new FlowableIllegalArgumentException("Booleans and null cannot be used in 'greater than or equal' condition");
                case LESS_THAN_OR_EQUAL:
                    throw new FlowableIllegalArgumentException("Booleans and null cannot be used in 'less than or equal' condition");
                default:
                    break;
            }

            if (operator == QueryOperator.EQUALS_IGNORE_CASE && !(value instanceof String)) {
                throw new FlowableIllegalArgumentException("Only string values can be used with 'equals ignore case' condition");
            }

            if (operator == QueryOperator.NOT_EQUALS_IGNORE_CASE && !(value instanceof String)) {
                throw new FlowableIllegalArgumentException("Only string values can be used with 'not equals ignore case' condition");
            }

            if ((operator == QueryOperator.LIKE || operator == QueryOperator.LIKE_IGNORE_CASE) && !(value instanceof String)) {
                throw new FlowableIllegalArgumentException("Only string values can be used with 'like' condition");

View on GitHub (pinned to d6d39ce1c6)