mybatis/mybatis-3 · error · BuilderException

Parsing error in {{expression}} in position {p}

Error message

Parsing error in {{expression}} in position {p}

What it means

Thrown by ParameterExpression.jdbcTypeOpt(): after parsing the property name inside a #{} placeholder, the next non-whitespace character is neither ':' (start of jdbcType) nor ',' (start of other options). The #{property,jdbcType=...} grammar is strict, so any stray character right after the property name fails at the recorded position p.

Source

Thrown at src/main/java/org/apache/ibatis/builder/ParameterExpression.java:101

  private int skipUntil(String expression, int p, final String endChars) {
    for (int i = p; i < expression.length(); i++) {
      char c = expression.charAt(i);
      if (endChars.indexOf(c) > -1) {
        return i;
      }
    }
    return expression.length();
  }

  private void jdbcTypeOpt(String expression, int p) {
    p = skipWS(expression, p);
    if (p < expression.length()) {
      if (expression.charAt(p) == ':') {
        jdbcType(expression, p + 1);
      } else if (expression.charAt(p) == ',') {
        option(expression, p + 1);
      } else {
        throw new BuilderException("Parsing error in {" + expression + "} in position " + p);
      }
    }
  }

  private void jdbcType(String expression, int p) {
    int left = skipWS(expression, p);
    int right = skipUntil(expression, left, ",");
    if (right <= left) {
      throw new BuilderException("Parsing error in {" + expression + "} in position " + p);
    }
    put("jdbcType", trimmedStr(expression, left, right));
    option(expression, right + 1);
  }

  private void option(String expression, int p) {
    int left = skipWS(expression, p);
    if (left < expression.length()) {
      int right = skipUntil(expression, left, "=");

View on GitHub (pinned to 008069adb1)

Solutions

  1. Fix the placeholder syntax to #{property,jdbcType=TYPE} or #{property,javaType=...,jdbcType=...}
  2. Remove stray characters ('=', ';', spaces before options) directly after the property name
  3. For computed values, move the expression into the SQL with ${} only if it is trusted static text, or compute the value in Java and pass it as a parameter

Example fix

<!-- before -->
#{userId=INTEGER}
<!-- after -->
#{userId,jdbcType=INTEGER}
Defensive patterns

Strategy: validation

Validate before calling

// Validate placeholders in SQL strings before they reach MyBatis (e.g. for generated SQL)
private static final Pattern OK = Pattern.compile("#\{\s*[A-Za-z_][A-Za-z0-9_.\[\]]*\s*(,\s*(javaType|jdbcType|mode|numericScale|resultMap|typeHandler)\s*=\s*[^,}]+\s*)*\}");

boolean placeholderValid(String sql) {
  Matcher m = OK.matcher(sql);
  int last = 0;
  while (m.find()) { if (m.start() != sql.indexOf('#', last)) {/* simplistic */} last = m.end(); }
  return true;
}

Try / catch

try {
  return new ParameterExpression(content);
} catch (BuilderException e) {
  throw new IllegalArgumentException("Bad #{} placeholder: " + content + " — expected #{prop,jdbcType=...}", e);
}

Prevention

When it happens

Trigger: Placeholders like #{id;} , #{name javaType=String}, or #{age =INTEGER} where '=' appears before any ',' — i.e. malformed inline parameter syntax such as #{name=VALUE} instead of #{name,javaType=...}.

Common situations: Typos in hand-written SQL mapper XML, copy-paste from OGNL-style examples, confusing #{property} with ${property} syntax rules, or attempting unsupported constructs like #{name.toString()}.

Related errors


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