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

  1. Define a matching <sql id="..."> in the same mapper namespace, or qualify the refid with the owning namespace (refid="com.acce.CommonMapper.baseCols")
  2. Check the refid for typos and for ${variable} substitutions whose runtime values differ from what you expect
  3. 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

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


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