quarkusio/quarkus · error · TemplateException

Unable to compare null operands [op1=, op2=]

Error message

Unable to compare null operands [op1=, op2=]

What it means

IfSectionHelper.compare() is used for <, >, <=, >= comparisons in {#if} blocks. It throws TemplateException("Unable to compare null operands [op1=..., op2=...]") when either operand is null, since null ordering is undefined in Qute comparisons.

Source

Thrown at independent-projects/qute/core/src/main/java/io/quarkus/qute/IfSectionHelper.java:510

                    throw new TemplateException("Not a binary operator: " + this);
            }
        }

        boolean equals(Object op1, Object op2) {
            if (Objects.equals(op1, op2)) {
                return true;
            }
            if (op1 != null && op2 != null && (op1 instanceof Number || op2 instanceof Number)) {
                // Both operands are not null and at least one of them is a number
                return getDecimal(op1).compareTo(getDecimal(op2)) == 0;
            }
            return false;
        }

        @SuppressWarnings({ "rawtypes", "unchecked" })
        boolean compare(Object op1, Object op2) {
            if (op1 == null || op2 == null) {
                throw new TemplateException("Unable to compare null operands [op1=" + op1 + ", op2=" + op2 + "]");
            }
            Comparable c1;
            Comparable c2;
            if (op1 instanceof Comparable && op1.getClass().equals(op2.getClass())) {
                c1 = (Comparable) op1;
                c2 = (Comparable) op2;
            } else {
                c1 = getDecimal(op1);
                c2 = getDecimal(op2);
            }
            int result = c1.compareTo(c2);
            switch (this) {
                case GE:
                    return result >= 0;
                case GT:
                    return result > 0;
                case LE:
                    return result <= 0;

View on GitHub (pinned to e1c734241f)

Solutions

  1. Guard the comparison with an existence check: {#if item.price != null && item.price > 10} or use the elvis operator (item.price ?: 0).
  2. Provide a default value in the model so the operand is never null.
  3. Use ==/!= (which handle null) instead of ordering operators when null is possible.

Example fix

// before
{#if order.date > cutoffDate} // order.date is null -> TemplateException
// after
{#if order.date != null && order.date > cutoffDate}
Defensive patterns

Strategy: validation

Validate before calling

// in template
{#if order.date != null && order.date > cutoffDate}
// or in model
Objects.requireNonNullElse(order.getDate(), Instant.EPOCH);

Type guard

boolean comparableValues(Object a, Object b) {
    return a != null && b != null
        && a instanceof Comparable && a.getClass().equals(b.getClass());
}

Try / catch

try {
    return condition.evaluate(ctx);
} catch (TemplateException ex) {
    if (ex.getMessage().startsWith("Unable to compare null operands")) {
        LOGGER.warn("null operand in {#if} comparison: {}", ex.getMessage());
        return false; // or rethrow after logging
    }
    throw ex;
}

Prevention

When it happens

Trigger: An {#if} expression like {#if item.price > 10} or {#if date1 le date2} where one side resolves to null at evaluation time.

Common situations: Optional/missing model properties not initialized; config values absent; query results null; nullable dates compared against a constant.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/8a26d857919e3e34. Report an issue: GitHub.