Magento 2.2.0 Invalid Document Element 'resource': The attribute 'title' is required but missing

To find the file with the missing attribute you can modify the following file

vendor/magento/framework/Config/Reader/Filesystem.php

add following code

foreach($configMerger->getDom()->getElementsByTagName('resource') as $element ){
    if (!$element->hasAttribute('title')) {
        var_dump($configMerger->getDom()->saveXML($element));
    }
}

to the method

protected function _readFiles($fileList)

so it should look like

protected function _readFiles($fileList)
{
    /** @var \Magento\Framework\Config\Dom $configMerger */
    $configMerger = null;
    foreach ($fileList as $key => $content) {
        try {
            if (!$configMerger) {
                $configMerger = $this->_createConfigMerger($this->_domDocumentClass, $content);
            } else {
                $configMerger->merge($content);
            }
        } catch (\Magento\Framework\Config\Dom\ValidationException $e) {
            throw new \Magento\Framework\Exception\LocalizedException(
                new \Magento\Framework\Phrase("Invalid XML in file %1:\n%2", [$key, $e->getMessage()])
            );
        }
    }
    if ($this->validationState->isValidationRequired()) {
        $errors = [];
        if ($configMerger && !$configMerger->validate($this->_schemaFile, $errors)) {
            foreach($configMerger->getDom()->getElementsByTagName('resource') as $element ){
                if (!$element->hasAttribute('title')) {
                    var_dump($configMerger->getDom()->saveXML($element));
                }
            }
            $message = "Invalid Document \n";
            throw new \Magento\Framework\Exception\LocalizedException(
                new \Magento\Framework\Phrase($message . implode("\n", $errors))
            );
        }
    }

    $output = [];
    if ($configMerger) {
        $output = $this->_converter->convert($configMerger->getDom());
    }
    return $output;
}

Now you will be able to see a dump of the wrong XML element.


My best guess is that you have a <resource>-tag in any etc/acl.xml-file with a missing title-attribute. Usually this would throw the issue that you could not create or edit a new role. I'm guessing that they created an extra check in 2.2 to detect this more earlier.

Better check the acl.xml-files of your own custom extensions and/or those of 3rd party extensions. Please note: the title-attribute is only required for new <resource>-nodes. So if your adding a new resource-node to an existing resource node (provided by another module) there is no need to add the title-attribute to those. For example:

<!-- provided by Magento_Backend, so they already have a title: -->
<resource id="Magento_Backend::stores">
    <resource id="Magento_Backend::stores_settings">
        <!-- provided by Magento_Config: -->
        <resource id="Magento_Config::config">
            <!-- New resource! Title is required: -->
            <resource id="Vendor_Module::config_general" title="Something" translate="title"/> 
        </resource>
    </resource>
</resource>

I had this same problem recently. A couple of observations and a workaround.

1) It would help immensely if instead of 'Invalid Document' it told you which document was invalid.

2) The setup:upgrade process should probably do some pre-flight checks to make sure it can do the upgrade.

Anyway, if you're stuck trying to find a missing title in the dozens of acl.xml files, but need to get a site back online quick, the quick workaround is to make the title attribute optional instead of required.

Edit vendor/magento/framework/Acl/etc/acl_merged.xsd

Find the line

<xs:attribute name="title" type="typeTitle" use="required" />

replace with

<xs:attribute name="title" type="typeTitle" use="optional" />

re-run magento setup:upgrade

You'll still need to find the plugin with the missing title at some point.