apache/maven · error · XmlPullParserException

Duplicated tag: 'extensions'

Error message

Duplicated tag: 'extensions'

What it means

CoreExtensionsXpp3Reader is the generated XML pull-parser for .mvn/extensions.xml. In strict mode it accepts exactly one root <extensions> element; after the first root has been parsed, encountering another START_TAG named 'extensions' at document level throws XmlPullParserException 'Duplicated tag: extensions'. In practice this means the file contains two concatenated root elements — malformed XML with more than one document root.

Source

Thrown at compat/maven-embedder/src/main/java/org/apache/maven/cli/internal/extension/model/io/xpp3/CoreExtensionsXpp3Reader.java:516

     * @param parser a parser object.
     * @param strict a strict object.
     * @throws IOException IOException if any.
     * @throws XmlPullParserException XmlPullParserException if
     * any.
     * @return CoreExtensions
     */
    public CoreExtensions read(XmlPullParser parser, boolean strict) throws IOException, XmlPullParserException {
        CoreExtensions coreExtensions = null;
        int eventType = parser.getEventType();
        boolean parsed = false;
        while (eventType != XmlPullParser.END_DOCUMENT) {
            if (eventType == XmlPullParser.START_TAG) {
                if (strict && !"extensions".equals(parser.getName())) {
                    throw new XmlPullParserException(
                            "Expected root element 'extensions' but found '" + parser.getName() + "'", parser, null);
                } else if (parsed) {
                    // fallback, already expected a XmlPullParserException due to invalid XML
                    throw new XmlPullParserException("Duplicated tag: 'extensions'", parser, null);
                }
                coreExtensions = parseCoreExtensions(parser, strict);
                coreExtensions.setModelEncoding(parser.getInputEncoding());
                parsed = true;
            }
            eventType = parser.next();
        }
        if (parsed) {
            return coreExtensions;
        }
        throw new XmlPullParserException(
                "Expected root element 'extensions' but found no element at all: invalid XML document", parser, null);
    } // -- CoreExtensions read( XmlPullParser, boolean )

    /**
     * @see XmlStreamReader
     *
     * @param reader a reader object.

View on GitHub (pinned to e4093d4e12)

Solutions

  1. Open .mvn/extensions.xml and merge everything into a single root: one <extensions> element containing all <extension> children.
  2. Validate the file as XML (xmllint --noout .mvn/extensions.xml) — any XML parser flags the double root immediately.
  3. Fix the generating script to write atomically (overwrite) rather than append.
  4. Re-run mvn to confirm the extension loads (optionally -X for detail).

Example fix

<!-- before (.mvn/extensions.xml): two roots -->
<extensions>
  <extension>...</extension>
</extensions>
<extensions>
  <extension>...</extension>
</extensions>

<!-- after: single merged root -->
<extensions>
  <extension>...</extension>
  <extension>...</extension>
</extensions>
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight XML sanity check for .mvn/extensions.xml
var factory = javax.xml.parsers.DocumentBuilderFactory.newInstance();
factory.setValidating(false);
var doc = factory.newDocumentBuilder().parse(file); // throws on double root / malformed XML
if (!"extensions".equals(doc.getDocumentElement().getTagName())) {
    throw new IllegalStateException("Root element must be <extensions>");
}

Try / catch

try {
    cli.doMain(args, workingDir, ...);
} catch (org.codehaus.plexus.util.xml.pull.XmlPullParserException e) {
    if ("Duplicated tag: 'extensions'".equals(e.getMessage())) {
        // merge the concatenated roots into one and retry
    }
    throw e;
}

Prevention

When it happens

Trigger: A .mvn/extensions.xml produced by concatenating two files (e.g. a template merge or double-append in a script), or manual editing that duplicated the <extensions>...</extensions> block. Copy-pasting a second extensions block below the first instead of merging entries.

Common situations: CI scripts that append extension entries with 'cat >>' and run twice, duplicating the whole root element. Editors/plugins auto-inserting a fresh skeleton below existing content. Merge conflicts resolved by keeping both sides' root elements.

Related errors


AI-assisted analysis of apache/maven@e4093d4e12 (2026-08-21). Data as JSON: /api/errors/eacccf2128d43d49. Report an issue: GitHub.