baomidou/mybatis-plus · error · BuilderException

Each XMLConfigBuilder can only be used once.

Error message

Each XMLConfigBuilder can only be used once.

What it means

Thrown by MybatisXMLConfigBuilder.parse() when parse() is called a second time on the same builder instance. The builder flips its parsed flag on the first run and is single-use by design: XML and state have already been consumed, so a second parse would double-register mappers and settings.

Source

Thrown at mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLConfigBuilder.java:108

    public MybatisXMLConfigBuilder(Class<? extends Configuration> configClass, InputStream inputStream, String environment,
                            Properties props) {
        this(configClass, new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
    }

    private MybatisXMLConfigBuilder(Class<? extends Configuration> configClass, XPathParser parser, String environment,
                             Properties props) {
        super(newConfig(configClass));
        ErrorContext.instance().resource("SQL Mapper Configuration");
        this.configuration.setVariables(props);
        this.parsed = false;
        this.environment = environment;
        this.parser = parser;
    }

    public Configuration parse() {
        if (parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        parsed = true;
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
    }

    private void parseConfiguration(XNode root) {
        try {
            // issue #117 read properties first
            propertiesElement(root.evalNode("properties"));
            Properties settings = settingsAsProperties(root.evalNode("settings"));
            loadCustomVfsImpl(settings);
            loadCustomLogImpl(settings);
            typeAliasesElement(root.evalNode("typeAliases"));
            pluginsElement(root.evalNode("plugins"));
            objectFactoryElement(root.evalNode("objectFactory"));
            objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            reflectorFactoryElement(root.evalNode("reflectorFactory"));

View on GitHub (pinned to bf67d90747)

Solutions

  1. Create a new MybatisXMLConfigBuilder (new SqlSessionFactoryBuilder().build(reader)) for each parse.
  2. Guard lazy initialization with a null/once check so parse() runs exactly once per builder.
  3. If the goal is two configurations, instantiate two builders from the same reader/resource.

Example fix

// before
private final XMLConfigBuilder builder = new XMLConfigBuilder(reader);
public SqlSessionFactory factory() { return new SqlSessionFactoryBuilder().build(builder.parse()); }
public SqlSessionFactory factoryAgain() { return new SqlSessionFactoryBuilder().build(builder.parse()); }

// after
public SqlSessionFactory factory() {
  try (Reader r = getResourceAsReader("mybatis-config.xml")) {
    return new SqlSessionFactoryBuilder().build(r);
  }
}
Defensive patterns

Strategy: type-guard

Type guard

// guard: track parse state per builder
if (builder != null && !parsedOnce) {
    configuration = builder.parse();
    parsedOnce = true;
}

Try / catch

Catch BuilderException 'Each XMLConfigBuilder can only be used once' as a signal of an initialization bug; instantiate a fresh builder (or SqlSessionFactoryBuilder().build(reader)) instead of retrying.

Prevention

When it happens

Trigger: Holding a MybatisXMLConfigBuilder (or SqlSessionFactoryBuilder flow that wraps it) and calling parse() twice: builder.parse(); ... builder.parse();

Common situations: Reusing a cached builder in a lazily-initialized singleton without a guard; copy-pasted initialization code paths both parsing the same builder; retry logic that re-invokes parse() after a partial failure.

Related errors


AI-assisted analysis of baomidou/mybatis-plus@bf67d90747 (2026-08-14). Data as JSON: /api/errors/022c0a993cdbd589. Report an issue: GitHub.