apache/maven · error · XMLStreamException

Expected root element '${rootTag}' but found '{}'

Error message

Expected root element '${rootTag}' but found '{}'

What it means

The classic modello STAX reader template (reader.vm, without the location-tracking/context features of reader-stax.vm) emits the same strict-mode root guard: in read(XMLStreamReader, strict), the first START_ELEMENT's local name must equal the model's root tag, otherwise the parse aborts with the local name that was found (parser.getLocalName() in this variant). It protects a model-specific reader from the wrong document type.

Source

Thrown at src/mdo/reader.vm:442

    /**
     * Method read.
     *
     * @param parser a parser object.
     * @param strict a strict object.
     * @throws IOException IOException if any.
     * @throws XMLStreamException XMLStreamException if
     * any.
     * @return ${root.name}
     */
    public ${root.name} read(XMLStreamReader parser, boolean strict) throws IOException, XMLStreamException {
        $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.getLocalName() + "'", parser.getLocation(), null);
                } else if (parsed) {
                    // fallback, already expected a XMLStreamException due to invalid XML
                    throw new XMLStreamException("Duplicated tag: '${rootTag}'", parser.getLocation(), null);
                }
                $rootLcapName = parse${rootUcapName}(parser, strict);
                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)

#foreach ( $class in $model.allClasses )
 #if ( $class.name != "InputSource" && $class.name != "InputLocation" )
  #set ( $classUcapName = $Helper.capitalise( $class.name ) )

View on GitHub (pinned to e4093d4e12)

Solutions

  1. Confirm the document type and root element match this reader's model before parsing.
  2. Use the reader generated for the document at hand (pom vs settings vs metadata).
  3. Peek the root local name and dispatch to the right reader.
  4. If lenient reading is acceptable, call read(parser, false) to skip the root-name check.

Example fix

// before
Model model = new MavenXpp3Reader().read(in, true); // in held settings.xml

// after
byte[] bytes = in.readAllBytes();
String root = peekRootLocalName(new ByteArrayInputStream(bytes));
if (!"project".equals(root)) {
    throw new IllegalArgumentException("Not a pom document, root element: " + root);
}
Model model = new MavenXpp3Reader().read(new ByteArrayInputStream(bytes), true);
Defensive patterns

Strategy: validation

Validate before calling

byte[] bytes = in.readAllBytes();
XMLInputFactory f = XMLInputFactory.newFactory();
f.setProperty(XMLInputFactory.SUPPORT_DTD, false);
XMLStreamReader peek = f.createXMLStreamReader(new ByteArrayInputStream(bytes));
String root = null;
while (peek.hasNext()) {
    if (peek.next() == XMLStreamReader.START_ELEMENT) { root = peek.getLocalName(); break; }
}
peek.close();
if (!"project".equals(root)) {
    throw new IllegalArgumentException("Not a pom document, root element: " + root);
}

Type guard

static boolean isRootElementMismatch(XMLStreamException e) {
    return e.getMessage() != null && e.getMessage().startsWith("Expected root element");
}

Try / catch

try {
    Model model = reader.read(parser, true);
} catch (XMLStreamException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Expected root element")) {
        // wrong document type for this reader; route elsewhere
    }
    throw e;
}

Prevention

When it happens

Trigger: read(parser, true) or read(InputStream, true) on XML whose root element local name differs from the expected root tag: settings.xml fed to a POM reader, an XML fragment like <dependency>, or correct vocabulary nested at the wrong level.

Common situations: Mixed XML types routed through one parser; readers generated from a different model version; POM snippets copy-pasted without the <project> wrapper; the wrong file variable passed to read().

Related errors


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