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.

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.

Appendix: 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)