flowable/flowable-engine · error · ELException

error.compare.types

error.compare.types

Error message

error.compare.types

What it means

BooleanOperations.lt0 performs '<' comparison on two operands after numeric coercion attempts fail. If neither operand is Comparable (and types are otherwise incomparable), it throws an ELException with 'error.compare.types' naming both classes.

Source

Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/de/odysseus/el/misc/BooleanOperations.java:65

		if (SIMPLE_FLOAT_TYPES.contains(t1) || SIMPLE_FLOAT_TYPES.contains(t2)) {
			return converter.convert(o1, Double.class) < converter.convert(o2, Double.class);
		}
		if (BigInteger.class.isAssignableFrom(t1) || BigInteger.class.isAssignableFrom(t2)) {
			return converter.convert(o1, BigInteger.class).compareTo(converter.convert(o2, BigInteger.class)) < 0;
		}
		if (SIMPLE_INTEGER_TYPES.contains(t1) || SIMPLE_INTEGER_TYPES.contains(t2)) {
			return converter.convert(o1, Long.class) < converter.convert(o2, Long.class);
		}
		if (t1 == String.class || t2 == String.class) {
			return converter.convert(o1, String.class).compareTo(converter.convert(o2, String.class)) < 0;
		}
		if (o1 instanceof Comparable) {
			return ((Comparable)o1).compareTo(o2) < 0;
		}
		if (o2 instanceof Comparable) {
			return ((Comparable)o2).compareTo(o1) > 0;
		}
		throw new ELException(LocalMessages.get("error.compare.types", o1.getClass(), o2.getClass()));
	}

	@SuppressWarnings("unchecked")
	private static final boolean gt0(TypeConverter converter, Object o1, Object o2) {		
		Class<?> t1 = o1.getClass();
		Class<?> t2 = o2.getClass();
		if (BigDecimal.class.isAssignableFrom(t1) || BigDecimal.class.isAssignableFrom(t2)) {
			return converter.convert(o1, BigDecimal.class).compareTo(converter.convert(o2, BigDecimal.class)) > 0;
		}
		if (SIMPLE_FLOAT_TYPES.contains(t1) || SIMPLE_FLOAT_TYPES.contains(t2)) {
			return converter.convert(o1, Double.class) > converter.convert(o2, Double.class);
		}
		if (BigInteger.class.isAssignableFrom(t1) || BigInteger.class.isAssignableFrom(t2)) {
			return converter.convert(o1, BigInteger.class).compareTo(converter.convert(o2, BigInteger.class)) > 0;
		}
		if (SIMPLE_INTEGER_TYPES.contains(t1) || SIMPLE_INTEGER_TYPES.contains(t2)) {
			return converter.convert(o1, Long.class) > converter.convert(o2, Long.class);
		}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Make one of the operand types implement Comparable (with matching comparison semantics).
  2. Coerce operands in the expression to comparable types (e.g. numbers or strings) before comparing.
  3. Rewrite the expression condition to compare a comparable field instead of the objects themselves.

Example fix

// before: #{user < otherUser}  (POJOs, not Comparable)
// after: #{user.id < otherUser.id}
// or make User implement Comparable<User> with compareTo comparing by id
Defensive patterns

Strategy: type-guard

Validate before calling

boolean comparable(Object o) { return o == null || o instanceof Comparable || o instanceof Number || o instanceof String; }
if (!comparable(a) || !comparable(b)) throw new IllegalArgumentException("operands of < must be Comparable");

Type guard

boolean isComparableOperand(Object o) {
    return o instanceof Comparable || o instanceof Number || o instanceof String;
}

Try / catch

try {
    boolean result = BooleanOperations.lt(converter, a, b);
} catch (ELException e) {
    // incomparable operand types; coerce or compare a comparable field instead
}

Prevention

When it happens

Trigger: Evaluating an EL comparison like a < b where a and b are non-numeric, non-Comparable objects (e.g. arbitrary POJOs, arrays), via lt() or ge().

Common situations: Comparing unrelated object types in an expression condition; a bean property's type changed between versions so a formerly Comparable value is now a POJO; comparing booleans or custom types without Comparable support.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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