angular/angular · warning

The XML file does not contain a <${rootNodeName}> root node.

Error message

The XML file does not contain a <${rootNodeName}> root node.

What it means

`canParseXml()` - shared by the XLIFF 1.2, XLIFF 2.0, and XTB parsers - parsed the file as XML without ERROR-level problems, but found no root element with the expected name (`xliff`, `xliffv2`, or `translationbundle`). It returns canParse=false with this warning so the loader can attempt the next parser.

Source

Thrown at packages/localize/tools/src/translate/translation_files/translation_parsers/translation_utils.ts:113

  rootNodeName: string,
  attributes: Record<string, string>,
): ParseAnalysis<XmlTranslationParserHint> {
  const diagnostics = new Diagnostics();
  const xmlParser = new XmlParser();
  const xml = xmlParser.parse(contents, filePath);

  if (
    xml.rootNodes.length === 0 ||
    xml.errors.some((error) => error.level === ParseErrorLevel.ERROR)
  ) {
    xml.errors.forEach((e) => addParseError(diagnostics, e));
    return {canParse: false, diagnostics};
  }

  const rootElements = xml.rootNodes.filter(isNamedElement(rootNodeName));
  const rootElement = rootElements[0];
  if (rootElement === undefined) {
    diagnostics.warn(`The XML file does not contain a <${rootNodeName}> root node.`);
    return {canParse: false, diagnostics};
  }

  for (const attrKey of Object.keys(attributes)) {
    const attr = rootElement.attrs.find((attr) => attr.name === attrKey);
    if (attr === undefined || attr.value !== attributes[attrKey]) {
      addParseDiagnostic(
        diagnostics,
        rootElement.sourceSpan,
        `The <${rootNodeName}> node does not have the required attribute: ${attrKey}="${attributes[attrKey]}".`,
        ParseErrorLevel.WARNING,
      );
      return {canParse: false, diagnostics};
    }
  }

  if (rootElements.length > 1) {
    xml.errors.push(

View on GitHub (pinned to 51cb07e980)

Solutions

  1. Open the file and check the actual root element and its version attribute (e.g. `<xliff version="1.2">` vs `<xliff version="2.0" xmlns="urn:oasis:names:tc:xliff:document:2.0">`).
  2. Regenerate the file with the serializer format that matches the file you intend to use (`ng extract-i18n --format=xlf|xlf2|xtb`).
  3. Keep the translate step's parser expectations aligned with the extraction format in one place (shared config).

Example fix

// before: declared XLIFF 1.2 flow, file is actually XLIFF 2.0
<xliff version="2.0" xmlns="urn:oasis:names:tc:xliff:document:2.0">...</xliff>

// after: match versions - use XLIFF 1.2
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">...</xliff>
// (or switch the whole pipeline to --format=xlf2)
Defensive patterns

Strategy: validation

Validate before calling

// Verify the XML root element matches the expected format before loading
function hasXmlRoot(contents: string, rootName: string): boolean {
  const match = contents.match(/^\s*<\?xml[^>]*\?>\s*<(?<root>[a-zA-Z0-9:_.-]+)/);
  return match?.groups?.root === rootName;
}
// XLIFF 1.2 root: 'xliff' with version="1.2"; XLIFF 2.0: 'xliff' version="2.0"; XTB: 'translationbundle'
if (!hasXmlRoot(contents, 'xliff')) throw new Error(`Unexpected root element in ${filePath}`);

Prevention

When it happens

Trigger: Passing an XML translation file whose root element does not match the format the parser expects: an XLIFF 2.0 file when the XLIFF 1.2 parser inspects it (or vice versa), an XTB file inspected by an XLIFF parser, or any XML doc with a different root.

Common situations: Mixing XLIFF versions between what the extraction produced and what translation was configured for; a vendor/tool exporting a wrapper XML; manually restructured files.

Related errors


AI-assisted analysis of angular/angular@51cb07e980 (2026-08-22). Data as JSON: /api/errors/465247cb50cc1dfb. Report an issue: GitHub.