apache/maven · error · XMLStreamException

Duplicated tag: '${rootTag}'

Error message

Duplicated tag: '${rootTag}'

What it means

After read() has parsed one root element, its event loop keeps scanning to END_DOCUMENT; a second START_ELEMENT at the top level means the stream contains more than one root element. Well-formed XML has exactly one root, so a conforming STAX parser normally rejects such input first - the template comment marks this throw as a fallback for lenient parsers or malformed streams that surface the second root as an event.

Source

Thrown at src/mdo/reader-stax.vm:279

     */
#if ( $locationTracking )
    public ${root.name} read(XMLStreamReader parser, boolean strict, InputSource inputSrc) throws XMLStreamException {
#else
    public ${root.name} read(XMLStreamReader parser, boolean strict) throws XMLStreamException {
#end
#if ( $needXmlContext )
        Deque<Object> context = new ArrayDeque<>();
#end
        $rootUcapName $rootLcapName = null;
        int eventType = parser.getEventType();
        boolean parsed = false;
        while (eventType != XMLStreamReader.END_DOCUMENT) {
            if (eventType == XMLStreamReader.START_ELEMENT) {
                if (strict && ! "${rootTag}".equals(parser.getLocalName())) {
                    throw new XMLStreamException("Expected root element '${rootTag}' but found '" + parser.getName() + "'", parser.getLocation(), null);
                } else if (parsed) {
                    // fallback, already expected a XMLStreamException due to invalid XML
                    throw new XMLStreamException("Duplicated tag: '${rootTag}'", parser.getLocation(), null);
                }
#if ( $locationTracking )
                $rootLcapName = parse${rootUcapName}(parser, strict, parser.getNamespaceURI(), inputSrc);
#elseif ( $needXmlContext )
                $rootLcapName = parse${rootUcapName}(parser, strict, parser.getNamespaceURI(), context);
#else
                $rootLcapName = parse${rootUcapName}(parser, strict, parser.getNamespaceURI());
#end
                parsed = true;
            }
            eventType = parser.next();
        }
        if (parsed) {
            return $rootLcapName;
        }
            throw new XMLStreamException("Expected root element '${rootTag}' but found no element at all: invalid XML document", parser.getLocation(), null);
    } //-- ${root.name} read(XMLStreamReader, boolean)

View on GitHub (pinned to e4093d4e12)

Solutions

  1. Fix the input so it contains exactly one top-level element - wrap multiple documents in a shared root or split the stream and parse each document with its own reader.
  2. If the source is concatenated documents, split on each <?xml declaration or on the known root tags and parse the parts separately.
  3. Pre-validate the stream with a standard STAX parser so multiple-root input fails early with clear context.

Example fix

// before: one InputStream holding two concatenated pom documents
reader.read(in, true); // throws Duplicated tag: 'project' at the second root

// after: split documents, parse each with its own reader
for (byte[] doc : splitXmlDocuments(in)) {
    Model model = reader.read(new ByteArrayInputStream(doc), true);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Fail fast on multi-root streams before calling the model reader
XMLInputFactory f = XMLInputFactory.newFactory();
f.setProperty(XMLInputFactory.SUPPORT_DTD, false);
XMLStreamReader p = f.createXMLStreamReader(new ByteArrayInputStream(bytes));
try {
    int roots = 0;
    while (p.hasNext()) {
        if (p.next() == XMLStreamReader.START_ELEMENT && ++roots > 1) {
            throw new IllegalStateException("Input has more than one root element");
        }
    }
} finally {
    p.close();
}

Try / catch

try {
    Model model = reader.read(parser, true);
} catch (XMLStreamException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Duplicated tag")) {
        // stream carried a second top-level element: fix the producer or split documents
    }
    throw e;
}

Prevention

When it happens

Trigger: read() over a stream holding two concatenated XML documents (e.g. two pom files appended together); a custom or lenient XMLStreamReader that does not enforce single-root well-formedness; also reachable with strict=false after one root was parsed and another top-level element follows.

Common situations: CI logs or test fixtures that join several XML snippets into one stream; files truncated and later appended; streaming pipelines that flush multiple documents into one channel; concatenation tools that merge XML without a wrapper element.

Related errors


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