{"record":{"id":"d28c620f1751d386","repo":"apache/cordova-android","slug":"androidmanifest-at-path-has-incorrect-root-node","errorCode":null,"errorMessage":"AndroidManifest at ${path} has incorrect root node name (expected \"manifest\")","messagePattern":"AndroidManifest at (.+?) has incorrect root node name \\(expected \"manifest\"\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/AndroidManifest.js","lineNumber":31,"sourceCode":"    software distributed under the License is distributed on an\n    \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n    KIND, either express or implied.  See the License for the\n    specific language governing permissions and limitations\n    under the License.\n*/\n\nconst fs = require('node:fs');\nconst xml = require('cordova-common').xmlHelpers;\n\nconst DEFAULT_ORIENTATION = 'default';\n\n/** Wraps an AndroidManifest file */\nclass AndroidManifest {\n    constructor (path) {\n        this.path = path;\n        this.doc = xml.parseElementtreeSync(path);\n        if (this.doc.getroot().tag !== 'manifest') {\n            throw new Error('AndroidManifest at ' + path + ' has incorrect root node name (expected \"manifest\")');\n        }\n    }\n\n    getVersionName () {\n        return this.doc.getroot().attrib['android:versionName'];\n    }\n\n    setVersionName (versionName) {\n        this.doc.getroot().attrib['android:versionName'] = versionName;\n        return this;\n    }\n\n    getVersionCode () {\n        return this.doc.getroot().attrib['android:versionCode'];\n    }\n\n    setVersionCode (versionCode) {\n        this.doc.getroot().attrib['android:versionCode'] = versionCode;","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/apache/cordova-android/blob/7c1e190064e349ffa4bbc6ac37b77cd773e4dbd3/lib/AndroidManifest.js#L13-L49","documentation":"The AndroidManifest wrapper class parses the given file with xmlHelpers.parseElementtreeSync in its constructor and requires the root element tag to be exactly 'manifest'. Thrown when the XML parses fine but is a different document type (root tag is 'widget', 'resources', 'html', etc.), i.e. you passed an XML file that is not an AndroidManifest.xml.","triggerScenarios":"`new AndroidManifest(path)` with a path to config.xml, a stringified XML resource, or any XML whose root element is not <manifest>. Typically hit by tooling that updates versionName/versionCode (setVersionName/setVersionName on this class) with a wrong path.","commonSituations":"Passing config.xml instead of platforms/android/app/src/main/AndroidManifest.xml; a copy/template step that wraps the manifest in another element; globbing the first *.xml in a directory.","solutions":["Pass the platform manifest path: platforms/android/app/src/main/AndroidManifest.xml.","Inspect the file's first element (head -1) to confirm it starts with <manifest ...>.","If the platform manifest is corrupted, regenerate it: `cordova platform rm android && cordova platform add android`."],"exampleFix":"// before\nconst manifest = new AndroidManifest('config.xml');\n\n// after\nconst manifest = new AndroidManifest(\n  path.join(projectRoot, 'platforms', 'android', 'app', 'src', 'main', 'AndroidManifest.xml')\n);","handlingStrategy":"validation","validationCode":"const { xmlHelpers } = require('cordova-common');\n\nfunction assertAndroidManifest (filePath) {\n  const doc = xmlHelpers.parseElementtreeSync(filePath);\n  if (doc.getroot().tag !== 'manifest') {\n    throw new Error(`${filePath} is not an AndroidManifest.xml (root: ${doc.getroot().tag})`);\n  }\n  return true;\n}","typeGuard":"const isManifestDoc = (doc) =>\n  Boolean(doc && typeof doc.getroot === 'function' && doc.getroot() && doc.getroot().tag === 'manifest');","tryCatchPattern":"try {\n  manifest = new AndroidManifest(candidatePath);\n} catch (e) {\n  throw new Error(`Refusing ${candidatePath}: ${e.message} - expected platforms/android/app/src/main/AndroidManifest.xml`);\n}","preventionTips":["Always derive the manifest path from the platform root (platforms/android/app/src/main/AndroidManifest.xml), never from user input or a glob.","Name any auxiliary XML files something other than AndroidManifest.xml to avoid path mix-ups."],"tags":["xml","android-manifest","parser","cordova-platform"],"backgroundTag":"xml-root-element-invalid","analyzedSha":"7c1e190064e349ffa4bbc6ac37b77cd773e4dbd3","analyzedAt":"2026-08-22T04:57:58.868Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}