mybatis/mybatis-3 · error · BuilderException
The expression '" + expression + "' evaluated to a null valu
Error message
The expression '" + expression + "' evaluated to a null value.
What it means
ExpressionEvaluator.evaluateIterable evaluates the collection expression of a <foreach> via OGNL. When nullable is false (the default foreach path) and the expression evaluates to null, there is nothing to iterate, so a BuilderException is thrown naming the expression. The nullable=true variant (used by <if>-style null-tolerant paths since 3.5.9) returns null instead; plain <foreach> does not.
Source
Thrown at src/main/java/org/apache/ibatis/scripting/xmltags/ExpressionEvaluator.java:61
/**
* @deprecated Since 3.5.9, use the {@link #evaluateIterable(String, Object, boolean)}.
*/
@Deprecated
public Iterable<?> evaluateIterable(String expression, Object parameterObject) {
return evaluateIterable(expression, parameterObject, false);
}
/**
* @since 3.5.9
*/
public Iterable<?> evaluateIterable(String expression, Object parameterObject, boolean nullable) {
Object value = OgnlCache.getValue(expression, parameterObject);
if (value == null) {
if (nullable) {
return null;
}
throw new BuilderException("The expression '" + expression + "' evaluated to a null value.");
}
if (value instanceof Iterable) {
return (Iterable<?>) value;
}
if (value.getClass().isArray()) {
// the array may be primitive, so Arrays.asList() may throw
// a ClassCastException (issue 209). Do the work manually
// Curse primitives! :) (JGB)
int size = Array.getLength(value);
List<Object> answer = new ArrayList<>();
for (int i = 0; i < size; i++) {
Object o = Array.get(value, i);
answer.add(o);
}
return answer;
}
if (value instanceof Map) {
return ((Map) value).entrySet();View on GitHub (pinned to 008069adb1)
Solutions
- Guard the foreach: <if test="items != null"> ... <foreach collection="items"...> ... </if>
- Default the collection to empty in the service/DTO: List<Item> items = Collections.emptyList(); or ids != null ? ids : Collections.emptyList()
- Fix the expression so it points at the real property name (@Param name, field name, or map key)
- For truly optional iterations, keep the whole SQL fragment conditional rather than passing null to foreach
Example fix
<!-- before -->
<foreach collection="ids" item="id">#{id}</foreach> <!-- ids is null -->
<!-- after -->
<if test="ids != null">
<foreach collection="ids" item="id">#{id}</foreach>
</if> Defensive patterns
Strategy: validation
Validate before calling
// in the service layer, before the call
List<Long> ids = Optional.ofNullable(req.getIds()).orElseGet(java.util.Collections::emptyList);
if (!ids.isEmpty()) { query.setIdList(ids); } // and skip the foreach branch otherwise Prevention
- Wrap every <foreach> in <if test="collection != null">
- Default collection parameters to empty lists in DTOs and @Param arguments
- Verify foreach collection names match @Param/property names exactly
When it happens
Trigger: <foreach collection="items" ...> when the parameter's items field is null; expression typo referencing a property that does not exist resolves to null; @Param("ids") list argument left null by the caller; optional query filters represented as null lists.
Common situations: Service methods with optional collection filters defaulting to null; API callers omitting list parameters; map-based parameters missing the foreach key; conditional building of query objects where some branches never set the list.
Related errors
- Error evaluating expression '" + expression + "'. Return va
- Error evaluating expression '" + expression + "'. Cause: " +
- Parsing error was found in mapping #{{content}}. Check synt
- There is no getter for property named '{}' in '{}'
- Invalid index syntax in property: '{}'. Missing closing brac
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/9e0a9987a0a19e00.
Report an issue: GitHub.