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
- Guard the comparison with an existence check: {#if item.price != null && item.price > 10} or use the elvis operator (item.price ?: 0).
- Provide a default value in the model so the operand is never null.
- 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
- Null-check operands before <, >, <=, >= in {#if} expressions
- Use elvis (?:) to supply defaults for possibly-null operands
- Initialize model/config properties used in ordered comparisons
- Prefer ==/!= (null-safe) when ordering is not required
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
- Unable to compare null operands [op1=, op2=]
- Invalid template link [${templateLink}] - Expeting a link th
- Templates that are not backed by a file must provide extensi
- Literal must not be null
- Expression is not a literal:
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/8a26d857919e3e34.
Report an issue: GitHub.