baomidou/mybatis-plus · error · BuilderException
Unknown element <%s> in SQL statement.
Error message
Unknown element <%s> in SQL statement.
What it means
While parsing a mixed dynamic SQL node, MybatisXMLScriptBuilder encountered an element child that is not one of the supported node handlers (trim, where, set, foreach, if, choose, when, otherwise, bind). Any other tag inside a SQL statement is rejected at parse time.
Source
Thrown at mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLScriptBuilder.java:171
if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
String text = cacheStr(child.getStringBody(""));
if (text.trim().isEmpty()) {
StaticTextSqlNode staticTextSqlNode = CACHE_EMPTY_SQL_NODE.computeIfAbsent(text, StaticTextSqlNode::new);
contents.add(staticTextSqlNode);
continue;
}
TextSqlNode textSqlNode = new TextSqlNode(text);
if (textSqlNode.isDynamic()) {
contents.add(textSqlNode);
isDynamic = true;
} else {
contents.add(new StaticTextSqlNode(text));
}
} else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
String nodeName = child.getNode().getNodeName();
NodeHandler handler = nodeHandlerMap.get(nodeName);
if (handler == null) {
throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
}
handler.handleNode(child, contents);
isDynamic = true;
}
}
return new MixedSqlNode(contents);
}
private interface NodeHandler {
void handleNode(XNode nodeToHandle, List<SqlNode> targetContents);
}
private class BindHandler implements NodeHandler {
public BindHandler() {
// Prevent Synthetic Access
}
@OverrideView on GitHub (pinned to bf67d90747)
Solutions
- Remove or rename the offending element named in the message to a supported one (if/choose/foreach/where/set/trim/bind)
- If the tag is reusable SQL text, move it into a <sql id="..."> fragment and reference it with <include refid="..."/>
- If the tag was a typo of a supported element, correct the tag name
Example fix
<!-- before --> <select id="list" resultType="User"> <columns>id, name</columns> FROM user </select> <!-- after --> <sql id="userCols">id, name</sql> <select id="list" resultType="User"> SELECT <include refid="userCols"/> FROM user </select>
Defensive patterns
Strategy: validation
Validate before calling
// During build, assert only whitelisted dynamic-SQL tags appear inside statements:
Set<String> OK = Set.of("if","choose","when","otherwise","trim","where","set","foreach","bind","include","sql");
NodeList kids = stmtElement.getChildNodes();
for (int i = 0; i < kids.getLength(); i++) {
Node n = kids.item(i);
if (n.getNodeType() == Node.ELEMENT_NODE && !OK.contains(n.getNodeName()))
throw new IllegalStateException("Unsupported tag <" + n.getNodeName() + "> in " + stmtElement.getAttribute("id"));
} Prevention
- Paste SQL as plain text inside the statement body, never rich/HTML markup
- Put reusable fragments in <sql id> and reference via <include>
- Validate mapper XML in CI with the official DTD
When it happens
Trigger: Placing a custom or mistaken element inside <select>/<update> SQL, e.g. <column>, <sql> include misuse, a stray HTML tag pasted in, or a typo like <iffe> instead of <if>; also custom tags intended for other templating engines left in the mapper XML.
Common situations: Copy-pasting SQL from documentation/HTML that carries formatting tags; dialect-specific XML fragments; a custom tag from a homegrown preprocessor that is no longer run.
Related errors
- Mapper's namespace cannot be empty
- Error parsing Mapper XML. The XML location is '%s'. Cause: %
- Too many default (otherwise) elements in choose statement.
- Ambiguous collection type for property '%s'. You must specif
- %s already contains value for %s
AI-assisted analysis of baomidou/mybatis-plus@bf67d90747 (2026-08-14).
Data as JSON: /api/errors/360d2ab854e68ce1.
Report an issue: GitHub.