mybatis/mybatis-3 · error · BuilderException

Mapper's namespace cannot be empty

Error message

Mapper's namespace cannot be empty

What it means

Thrown by XMLMapperBuilder.configurationElement() when the <mapper> root element of a mapper XML file has no namespace attribute or an empty one. The namespace is the mapper's identity: it binds the XML to a Java interface and prefixes every statement id, fragment id and cache-ref, so an empty namespace is fatal. Note this is usually re-wrapped as 'Error parsing Mapper XML' (error 67) with this as the cause.

Source

Thrown at src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java:122

    if (!configuration.isResourceLoaded(resource)) {
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }
    configuration.parsePendingResultMaps(false);
    configuration.parsePendingCacheRefs(false);
    configuration.parsePendingStatements(false);
  }

  public XNode getSqlFragment(String refid) {
    return sqlFragments.get(refid);
  }

  private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.isEmpty()) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  }

  private void buildStatementFromContext(List<XNode> list) {
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);

View on GitHub (pinned to 008069adb1)

Solutions

  1. Set namespace to the fully-qualified name of the corresponding mapper interface, e.g. <mapper namespace="com.acme.UserMapper">
  2. Ensure the interface exists and its package/name match the namespace exactly (case-sensitive)
  3. If the mapper is XML-only with no interface, you still need a unique non-empty namespace string

Example fix

<!-- before -->
<mapper>
  <select id="findById" resultType="User">...</select>
</mapper>

<!-- after -->
<mapper namespace="com.acme.UserMapper">
  <select id="findById" resultType="User">...</select>
</mapper>
Defensive patterns

Strategy: validation

Validate before calling

String ns = mapperRoot.getAttribute("namespace");
if (ns == null || ns.trim().isEmpty()) throw new IllegalStateException("mapper XML missing namespace");
if (!ns.matches("(?:[a-zA-Z_$][\\w$]*\\.)+[a-zA-Z_$][\\w$]*")) warn("namespace looks unlike an FQCN: " + ns);

Try / catch

catch BuilderException at build and surface the resource path; a missing namespace is a build-time fix, not a runtime recovery.

Prevention

When it happens

Trigger: <mapper> without a namespace attribute: <mapper>...</mapper>; or namespace="" (empty string). Raised when the mapper XML is parsed, whether loaded via <mapper resource=...>, url, or automatically alongside an interface.

Common situations: New hand-written mapper XML from a blank template; trimming the file and deleting the root element's attributes; namespace typed as a path (slashes) or with leading/trailing whitespace-only content; mapper XML placed in the same package as the interface but with missing namespace during annotation+XML mixed setups.

Related errors


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