{"record":{"id":"95e14ea5f784d689","repo":"SonarSource/sonarqube","slug":"backup-xml-is-not-valid-root-element-must-be-pro","errorCode":null,"errorMessage":"Backup XML is not valid. Root element must be <profile>.","messagePattern":"Backup XML is not valid\\. Root element must be <profile>\\.","errorType":"http","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileParser.java","lineNumber":134,"sourceCode":"          .prop(ATTRIBUTE_PARAMETER_VALUE, param.getValue())\n          .end();\n      }\n      xml.end(ATTRIBUTE_PARAMETERS);\n      xml.end(ATTRIBUTE_RULE);\n    }\n    xml.end(ATTRIBUTE_RULES).end(ATTRIBUTE_PROFILE).close();\n  }\n\n  public ImportedQProfile readXml(Reader reader) {\n    List<ImportedRule> rules = new ArrayList<>();\n    String profileName = null;\n    String profileLang = null;\n    try {\n      SMInputFactory inputFactory = initStax();\n      SMHierarchicCursor rootC = inputFactory.rootElementCursor(reader);\n      rootC.advance(); // <profile>\n      if (!ATTRIBUTE_PROFILE.equals(rootC.getLocalName())) {\n        throw new IllegalArgumentException(\"Backup XML is not valid. Root element must be <profile>.\");\n      }\n      SMInputCursor cursor = rootC.childElementCursor();\n\n      while (cursor.getNext() != null) {\n        String nodeName = cursor.getLocalName();\n        if (CS.equals(ATTRIBUTE_NAME, nodeName)) {\n          profileName = StringUtils.trim(cursor.collectDescendantText(false));\n        } else if (CS.equals(ATTRIBUTE_LANGUAGE, nodeName)) {\n          profileLang = StringUtils.trim(cursor.collectDescendantText(false));\n        } else if (CS.equals(ATTRIBUTE_RULES, nodeName)) {\n          SMInputCursor rulesCursor = cursor.childElementCursor(\"rule\");\n          rules = parseRuleActivations(rulesCursor);\n        }\n      }\n    } catch (XMLStreamException e) {\n      throw new IllegalArgumentException(\"Fail to restore Quality profile backup, XML document is not well formed\", e);\n    }\n    return new ImportedQProfile(profileName, profileLang, rules);","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileParser.java#L116-L152","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-export the profile with GET api/qualityprofiles/backup?language=...&qualityProfile=... and restore that file unmodified.","Edit the XML so the root element is <profile> if you intentionally restructured it.","Validate the XML root locally before upload: parse and assert getDocumentElement().getTagName().equals(\"profile\").","Ensure you are not uploading an export of ALL profiles where the expected single-profile root differs."],"exampleFix":"// before\nrestoreProfile(readFile(\"profiles.xml\")); // root is <profiles>\n// after\nDocument doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File(\"profiles.xml\"));\nif (!\"profile\".equals(doc.getDocumentElement().getTagName())) {\n  throw new IllegalStateException(\"not a single-profile backup file\");\n}\nrestoreProfile(new ByteArrayInputStream(serialized(doc)));","handlingStrategy":"validation","validationCode":"Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream);\nif (!\"profile\".equals(doc.getDocumentElement().getTagName())) {\n  throw new IllegalStateException(\"backup root element must be <profile>\");\n}","typeGuard":"static boolean isProfileBackup(File f) throws Exception {\n  Document d = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(f);\n  return \"profile\".equals(d.getDocumentElement().getTagName());\n}","tryCatchPattern":"try {\n  restoreProfile(stream);\n} catch (SonarQubeClientException e) {\n  if (String.valueOf(e.getMessage()).contains(\"Root element must be <profile>\")) {\n    LOG.error(\"file is not a single-profile backup; re-export via api/qualityprofiles/backup\");\n  }\n  throw e;\n}","preventionTips":["Always obtain backups from GET api/qualityprofiles/backup and restore them unmodified","Validate the root element locally before any upload","Beware BOM/encoding issues when editing XML by hand"],"tags":["sonarqube","webapi","quality-profile","xml","validation"],"backgroundTag":"schema-validation-failed","analyzedSha":"184c821202192afc1c599fc912d0889b69fffa53","analyzedAt":"2026-09-09T12:23:51.573Z","contentChangedAt":"2026-09-09T12:23:51.573Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}