mybatis/mybatis-3 · error · BuilderException

Error evaluating expression '" + expression + "'. Cause: " +

Error message

Error evaluating expression '" + expression + "'. Cause: " + e

What it means

OgnlCache.getValue() wraps any OGNL runtime failure in a BuilderException whose message embeds the failing expression and the original OgnlException. This is the generic failure point for every OGNL evaluation MyBatis performs: test="..." conditions in <if>/<when>, and ${...} string substitutions. The cause chain is preserved, so the root reason (syntax error, missing property, null navigation, wrong method signature) is in the nested exception.

Source

Thrown at src/main/java/org/apache/ibatis/scripting/xmltags/OgnlCache.java:49

 *
 * @see <a href='https://github.com/mybatis/old-google-code-issues/issues/342'>Issue 342</a>
 */
public final class OgnlCache {

  private static final OgnlMemberAccess MEMBER_ACCESS = new OgnlMemberAccess();
  private static final OgnlClassResolver CLASS_RESOLVER = new OgnlClassResolver();
  private static final Map<String, Object> expressionCache = new ConcurrentHashMap<>();

  private OgnlCache() {
    // Prevent Instantiation of Static Class
  }

  public static Object getValue(String expression, Object root) {
    try {
      OgnlContext context = Ognl.createDefaultContext(root, MEMBER_ACCESS, CLASS_RESOLVER, null);
      return Ognl.getValue(parseExpression(expression), context, root);
    } catch (OgnlException e) {
      throw new BuilderException("Error evaluating expression '" + expression + "'. Cause: " + e, e);
    }
  }

  private static Object parseExpression(String expression) throws OgnlException {
    Object node = expressionCache.get(expression);
    if (node == null) {
      node = Ognl.parseExpression(expression);
      expressionCache.put(expression, node);
    }
    return node;
  }

}

View on GitHub (pinned to 008069adb1)

Solutions

  1. Read the nested Cause in the stack trace: it names the exact property/method OGNL could not resolve.
  2. Align the property path in test= or ${} with the actual parameter (check @Param annotations on the mapper method).
  3. For null-safety, guard navigation: test="user != null and user.name != null".
  4. Add the missing getter or @Param to the parameter object.

Example fix

// before
<if test="name != null">AND name = #{name}</if>
<!-- mapper: find(@Param('userName') String name) -->
// after
<if test="userName != null">AND name = #{userName}</if>
Defensive patterns

Strategy: try-catch

Try / catch

try { return sqlSession.selectList(stmt, param); }
catch (BuilderException e) {
  Throwable root = e.getCause(); // OgnlException with the real reason
  log.error("OGNL failed for {}: {}", stmt, root.getMessage());
  throw e;
}

Prevention

When it happens

Trigger: A test="usr.name != null" where the parameter has no getName(); OGNL syntax errors like test="a == && b"; calling a method that does not exist in test="list.size() > 0" when the property is not a list; navigating into a null intermediate object with an invalid path; using ${} with a property name that is absent from the parameter map.

Common situations: Renaming a Java field/getter without updating mapper XML; copy-pasting an <if test> from another statement with a different parameter name; @Param name mismatch between mapper interface and XML; upgrading MyBatis/OGNL versions where grammar got stricter; null parameter passed to a dynamic statement.

Related errors


AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14). Data as JSON: /api/errors/a25b17bc0c18c764. Report an issue: GitHub.