mybatis/mybatis-3 · error · IncompleteElementException
Could not find SQL statement to include with refid '{refid}'
Error message
Could not find SQL statement to include with refid '{refid}' What it means
Thrown by XMLIncludeTransformer.findSqlFragment() when an <include refid="..."/> in a mapper XML cannot resolve to any <sql id="..."/> fragment. The refid is first expanded for ${} variables and then qualified with the current namespace, so both the fragment's existence and its fully-qualified name must match. It is wrapped as IncompleteElementException, which the builder may retry later (parsePending) before failing permanently.
Source
Thrown at src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java:101
NodeList children = source.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
applyIncludes(children.item(i), variablesContext, included);
}
} else if (included && (source.getNodeType() == Node.TEXT_NODE || source.getNodeType() == Node.CDATA_SECTION_NODE)
&& !variablesContext.isEmpty()) {
// replace variables in text node
source.setNodeValue(PropertyParser.parse(source.getNodeValue(), variablesContext));
}
}
private Node findSqlFragment(String refid, Properties variables) {
refid = PropertyParser.parse(refid, variables);
refid = builderAssistant.applyCurrentNamespace(refid, true);
try {
XNode nodeToInclude = configuration.getSqlFragments().get(refid);
return nodeToInclude.getNode().cloneNode(true);
} catch (IllegalArgumentException e) {
throw new IncompleteElementException("Could not find SQL statement to include with refid '" + refid + "'", e);
}
}
private String getStringAttribute(Node node, String name) {
return node.getAttributes().getNamedItem(name).getNodeValue();
}
/**
* Read placeholders and their values from include node definition.
*
* @param node
* Include node instance
* @param inheritedVariablesContext
* Current context used for replace variables in new variables values
*
* @return variables context from include instance (no inherited values)
*/
private Properties getVariablesContext(Node node, Properties inheritedVariablesContext) {View on GitHub (pinned to 008069adb1)
Solutions
- Define a matching <sql id="..."> in the same mapper namespace, or qualify the refid with the owning namespace (refid="com.acce.CommonMapper.baseCols")
- Check the refid for typos and for ${variable} substitutions whose runtime values differ from what you expect
- Ensure the mapper XML that declares the fragment is actually registered in <mappers> and parses cleanly (a failed mapper leaves pending includes that surface as this error)
Example fix
<!-- before --> <sql id="baseColumns">id, name</sql> <select id="findAll">SELECT <include refid="baseColums"/> FROM user</select> <!-- after --> <sql id="baseColumns">id, name</sql> <select id="findAll">SELECT <include refid="baseColumns"/> FROM user</select>
Defensive patterns
Strategy: validation
Validate before calling
// after parsing, before use: verify every include refid resolves
Set<String> ids = sqlFragments.keySet(); // qualified ids like 'ns.cols'
for (String refid : includeRefids) {
String qualified = refid.contains(".") ? refid : namespace + "." + refid;
if (!ids.contains(qualified)) throw new IllegalStateException("Unresolvable include refid: " + refid);
} Try / catch
catch (PersistenceException e) { if (e.getCause() instanceof IncompleteElementException) report missing fragment refid; } — but prefer fixing the mapper XML; the builder already retries pending includes. Prevention
- Keep <sql> fragments and their <include> refs in one review unit
- Prefer fully-qualified refids for cross-mapper fragments
- Run a smoke test that builds the SqlSessionFactory in CI to catch pending includes
When it happens
Trigger: <include refid="cols"/> where no <sql id="cols"> exists in the same namespace and no fully-qualified refid='com.acme.UserMapper.cols' fragment exists; a refid built from a ${variable} that resolves to an unexpected value; a <sql> fragment defined in a mapper that itself failed to load (bad XML, missing from <mappers>).
Common situations: Renaming or deleting a shared <sql> fragment but not the <include> references; moving mappers between packages and forgetting that unqualified refids resolve only within the same namespace; circular mapper includes; a typo in refid; the fragment lives in a mapper XML that was never registered in <mappers>.
Related errors
- Variable {name} defined twice in the same include definition
- Mapper's namespace cannot be empty
- Error parsing Mapper XML. The XML location is '{resource}'.
- Unknown execution method for: {name}
- Mapper method '{name}' attempted to return null from a metho
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/a0a6f35e977654fb.
Report an issue: GitHub.