SonarSource/sonarqube · error · IllegalArgumentException

Backup XML is not valid. Root element must be <profile>.

Error message

Backup XML is not valid. Root element must be <profile>.

What it means

QProfileParser.readXml validates the root element of an uploaded quality profile backup; the StAX cursor's first local name must be exactly 'profile'. Any other root element means the file is not a valid SonarQube profile backup and IllegalArgumentException is thrown.

Source

Thrown at server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileParser.java:134

          .prop(ATTRIBUTE_PARAMETER_VALUE, param.getValue())
          .end();
      }
      xml.end(ATTRIBUTE_PARAMETERS);
      xml.end(ATTRIBUTE_RULE);
    }
    xml.end(ATTRIBUTE_RULES).end(ATTRIBUTE_PROFILE).close();
  }

  public ImportedQProfile readXml(Reader reader) {
    List<ImportedRule> rules = new ArrayList<>();
    String profileName = null;
    String profileLang = null;
    try {
      SMInputFactory inputFactory = initStax();
      SMHierarchicCursor rootC = inputFactory.rootElementCursor(reader);
      rootC.advance(); // <profile>
      if (!ATTRIBUTE_PROFILE.equals(rootC.getLocalName())) {
        throw new IllegalArgumentException("Backup XML is not valid. Root element must be <profile>.");
      }
      SMInputCursor cursor = rootC.childElementCursor();

      while (cursor.getNext() != null) {
        String nodeName = cursor.getLocalName();
        if (CS.equals(ATTRIBUTE_NAME, nodeName)) {
          profileName = StringUtils.trim(cursor.collectDescendantText(false));
        } else if (CS.equals(ATTRIBUTE_LANGUAGE, nodeName)) {
          profileLang = StringUtils.trim(cursor.collectDescendantText(false));
        } else if (CS.equals(ATTRIBUTE_RULES, nodeName)) {
          SMInputCursor rulesCursor = cursor.childElementCursor("rule");
          rules = parseRuleActivations(rulesCursor);
        }
      }
    } catch (XMLStreamException e) {
      throw new IllegalArgumentException("Fail to restore Quality profile backup, XML document is not well formed", e);
    }
    return new ImportedQProfile(profileName, profileLang, rules);

View on GitHub (pinned to 184c821202)

Solutions

  1. Re-export the profile with GET api/qualityprofiles/backup?language=...&qualityProfile=... and restore that file unmodified.
  2. Edit the XML so the root element is <profile> if you intentionally restructured it.
  3. Validate the XML root locally before upload: parse and assert getDocumentElement().getTagName().equals("profile").
  4. Ensure you are not uploading an export of ALL profiles where the expected single-profile root differs.

Example fix

// before
restoreProfile(readFile("profiles.xml")); // root is <profiles>
// after
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File("profiles.xml"));
if (!"profile".equals(doc.getDocumentElement().getTagName())) {
  throw new IllegalStateException("not a single-profile backup file");
}
restoreProfile(new ByteArrayInputStream(serialized(doc)));
Defensive patterns

Strategy: validation

Validate before calling

Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream);
if (!"profile".equals(doc.getDocumentElement().getTagName())) {
  throw new IllegalStateException("backup root element must be <profile>");
}

Type guard

static boolean isProfileBackup(File f) throws Exception {
  Document d = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(f);
  return "profile".equals(d.getDocumentElement().getTagName());
}

Try / catch

try {
  restoreProfile(stream);
} catch (SonarQubeClientException e) {
  if (String.valueOf(e.getMessage()).contains("Root element must be <profile>")) {
    LOG.error("file is not a single-profile backup; re-export via api/qualityprofiles/backup");
  }
  throw e;
}

Prevention

When it happens

Trigger: POST api/qualityprofiles/restore with XML whose root element is <profiles>, <qualityProfile>, or anything other than <profile>; uploading a truncated or wrapped XML file; posting HTML/JSON error pages saved as XML.

Common situations: Hand-edited backups; files concatenated or wrapped by an aggregation step; downloading the wrong endpoint (a list of profiles rather than a single profile export); character-set/BOM issues that break root detection.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09). Data as JSON: /api/errors/95e14ea5f784d689. Report an issue: GitHub.