{"record":{"id":"022c0a993cdbd589","repo":"baomidou/mybatis-plus","slug":"each-xmlconfigbuilder-can-only-be-used-once","errorCode":null,"errorMessage":"Each XMLConfigBuilder can only be used once.","messagePattern":"Each XMLConfigBuilder can only be used once\\.","errorType":"exception","errorClass":"BuilderException","httpStatus":null,"severity":"error","filePath":"mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLConfigBuilder.java","lineNumber":108,"sourceCode":"\n    public MybatisXMLConfigBuilder(Class<? extends Configuration> configClass, InputStream inputStream, String environment,\n                            Properties props) {\n        this(configClass, new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);\n    }\n\n    private MybatisXMLConfigBuilder(Class<? extends Configuration> configClass, XPathParser parser, String environment,\n                             Properties props) {\n        super(newConfig(configClass));\n        ErrorContext.instance().resource(\"SQL Mapper Configuration\");\n        this.configuration.setVariables(props);\n        this.parsed = false;\n        this.environment = environment;\n        this.parser = parser;\n    }\n\n    public Configuration parse() {\n        if (parsed) {\n            throw new BuilderException(\"Each XMLConfigBuilder can only be used once.\");\n        }\n        parsed = true;\n        parseConfiguration(parser.evalNode(\"/configuration\"));\n        return configuration;\n    }\n\n    private void parseConfiguration(XNode root) {\n        try {\n            // issue #117 read properties first\n            propertiesElement(root.evalNode(\"properties\"));\n            Properties settings = settingsAsProperties(root.evalNode(\"settings\"));\n            loadCustomVfsImpl(settings);\n            loadCustomLogImpl(settings);\n            typeAliasesElement(root.evalNode(\"typeAliases\"));\n            pluginsElement(root.evalNode(\"plugins\"));\n            objectFactoryElement(root.evalNode(\"objectFactory\"));\n            objectWrapperFactoryElement(root.evalNode(\"objectWrapperFactory\"));\n            reflectorFactoryElement(root.evalNode(\"reflectorFactory\"));","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/baomidou/mybatis-plus/blob/bf67d907478c724120bf76292da54abf9e73c2b3/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLConfigBuilder.java#L90-L126","documentation":"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.","triggerScenarios":"Holding a MybatisXMLConfigBuilder (or SqlSessionFactoryBuilder flow that wraps it) and calling parse() twice: builder.parse(); ... builder.parse();","commonSituations":"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.","solutions":["Create a new MybatisXMLConfigBuilder (new SqlSessionFactoryBuilder().build(reader)) for each parse.","Guard lazy initialization with a null/once check so parse() runs exactly once per builder.","If the goal is two configurations, instantiate two builders from the same reader/resource."],"exampleFix":"// before\nprivate final XMLConfigBuilder builder = new XMLConfigBuilder(reader);\npublic SqlSessionFactory factory() { return new SqlSessionFactoryBuilder().build(builder.parse()); }\npublic SqlSessionFactory factoryAgain() { return new SqlSessionFactoryBuilder().build(builder.parse()); }\n\n// after\npublic SqlSessionFactory factory() {\n  try (Reader r = getResourceAsReader(\"mybatis-config.xml\")) {\n    return new SqlSessionFactoryBuilder().build(r);\n  }\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// guard: track parse state per builder\nif (builder != null && !parsedOnce) {\n    configuration = builder.parse();\n    parsedOnce = true;\n}","tryCatchPattern":"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.","preventionTips":["Treat builders as single-use; create them inside the factory method.","Use double-checked locking or holder idiom for lazy SqlSessionFactory singletons."],"tags":["mybatis","mybatis-plus","xml-config","single-use","initialization"],"backgroundTag":null,"analyzedSha":"bf67d907478c724120bf76292da54abf9e73c2b3","analyzedAt":"2026-08-14T15:17:09.543Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}