apache/maven · error · XMLStreamException
Expected root element '${rootTag}' but found '{}'
Error message
Expected root element '${rootTag}' but found '{}' What it means
Thrown by the STAX reader that modello generates from reader-stax.vm (the template behind readers like MavenXpp3Reader). In read(XMLStreamReader, strict), the first START_ELEMENT's local name must equal the model's root tag (the ${rootTag} placeholder, e.g. 'project' for the POM model); with strict=true any other name aborts the parse and the message reports the element actually found (as a QName, via parser.getName()). It is the reader's first guard against handing a model-specific parser the wrong kind of XML document.
Source
Thrown at src/mdo/reader-stax.vm:276
* @throws XMLStreamException XMLStreamException if
* any.
* @return ${root.name}
*/
#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;
}View on GitHub (pinned to e4093d4e12)
Solutions
- Verify the input is the document type this reader was generated for and that its root element matches the expected root tag (e.g. <project> for a pom).
- Use the reader matching the document: SettingsXpp3Reader for settings.xml, MetadataXpp3Reader for maven-metadata.xml, MavenXpp3Reader for pom.xml.
- Peek the root element local name before calling read() and branch to the correct reader.
- Only if lenient reading is acceptable, call read(parser, false) - the root-name check is skipped in non-strict mode.
Example fix
// before
Model model = new MavenXpp3Reader().read(in, true);
// in actually held settings.xml -> Expected root element 'project' but found '<settings>'
// 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);
}
Model model = new MavenXpp3Reader().read(new ByteArrayInputStream(bytes), true); 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")) {
throw new IllegalArgumentException("Wrong document type for this reader: " + e.getMessage(), e);
}
throw e;
} Prevention
- Route each XML document type to the reader generated for its model (pom vs settings vs metadata).
- Peek the root element local name before handing any document to a model reader.
- Keep model definitions, generated readers, and documents on matching versions.
- Log the offending root name from the exception message to spot misrouted files.
When it happens
Trigger: Calling read(InputStream/Reader, true) or read(XMLStreamReader, true) on a document whose root element is not the model's root tag: passing settings.xml or maven-metadata.xml to a POM reader, passing a fragment such as <modules> or <dependency> instead of a full document, or a root with the right namespace but a different local name.
Common situations: Routing mixed XML file types through one generated reader; using a reader built from a model version whose root tag changed; feeding POM snippets copied without the <project> wrapper; pipelines passing the wrong variable to read().
Related errors
- Expected root element '${rootTag}' but found '{}'
- Duplicated tag: '${rootTag}'
- Duplicated tag: '{}'
- Duplicated tag: '${rootTag}'
- Duplicated tag: '{}'
AI-assisted analysis of apache/maven@e4093d4e12 (2026-08-21).
Data as JSON: /api/errors/f78a78049195ab1e.
Report an issue: GitHub.