quarkusio/quarkus · error · TemplateException
Unable to compare null operands [op1=, op2=]
Error message
Unable to compare null operands [op1=, op2=]
What it means
When a {when}/{if} comparison operator (e.g. >, >=) is evaluated and either operand is null, Qute cannot order the values and throws TemplateException from compare(). Equality-style checks handle null fine, but relational comparison of null is rejected deliberately rather than returning a silently wrong result.
Source
Thrown at independent-projects/qute/core/src/main/java/io/quarkus/qute/WhenSectionHelper.java:261
return !Objects.equals(value, params.get(0));
case GE:
case GT:
case LE:
case LT:
return compare(value, params.get(0));
case IN:
return params.contains(value);
case NOT_IN:
return !params.contains(value);
default:
throw new TemplateException("Not a legal operator: " + this);
}
}
@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 = Operator.getDecimal(op1);
c2 = Operator.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 a null check first, e.g. {#if item.price != null && item.price > 10}.
- Use Qute's null-safe / default operators to coerce the operand, e.g. {item.price ?: 0} before comparing.
- Provide non-null defaults in the data model when building the template data.
- Restructure the {when} to compare only after an {#is} null check branch.
Example fix
// before
{#if order.discount > 0}...{/}
// after
{#if order.discount != null && order.discount > 0}...{/}
// or
{#if (order.discount ?: 0) > 0}...{/} Defensive patterns
Strategy: validation
Validate before calling
// in template
{#if item.price != null && item.price > 10}...{/}
// in Java before rendering
Objects.requireNonNullElse(item.getPrice(), BigDecimal.ZERO); Type guard
boolean isComparable(Object a, Object b) {
return a != null && b != null && a.getClass().equals(b.getClass()) && a instanceof Comparable;
} Try / catch
try {
renderTemplate(tpl, data);
} catch (TemplateException e) {
if (e.getMessage().startsWith("Unable to compare null operands")) {
// add null guards / defaults in the template or data model
}
} Prevention
- Always guard relational comparisons with null checks in templates
- Use the '?:' default operator to coerce possibly-null operands
- Provide non-null defaults in the data model for optional fields
When it happens
Trigger: Evaluating expressions like {#when item.price > 10} or {#if a >= b} where item.price or b resolves to null (missing property, null value passed in the data map).
Common situations: Optional data not supplied to the template; properties absent on some objects in a collection; API/model changes that made a field nullable; forgetting the null-safe '?elvis' style access before comparing.
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/5b981c6328adb47f.
Report an issue: GitHub.