Admin Product Page is broken: «Element with ID 'catalog.product.edit.tab.downloadable.links' already exists»

When I open any product (all my products are virtual) in admin interface, I got the error below:

Element with ID 'catalog.product.edit.tab.downloadable.links' already exists.
#0 lib\internal\Magento\Framework\View\Layout\Data\Structure.php(56): Magento\Framework\Data\Structure->createElement('catalog.product...', Array)
#1 lib\internal\Magento\Framework\View\Layout.php(721): Magento\Framework\View\Layout\Data\Structure->createStructuralElement('catalog.product...', 'block', 'Magento\Downloa...')
#2 var\generation\Magento\Framework\View\Layout\Interceptor.php(349): Magento\Framework\View\Layout->createBlock('Magento\Downloa...', 'catalog.product...', Array)
#3 app\code\Magento\Downloadable\Block\Adminhtml\Catalog\Product\Edit\Tab\Downloadable.php(165): Magento\Framework\View\Layout\Interceptor->createBlock('Magento\Downloa...', 'catalog.product...')
#4 lib\internal\Magento\Framework\View\Element\AbstractBlock.php(652): Magento\Downloadable\Block\Adminhtml\Catalog\Product\Edit\Tab\Downloadable->_toHtml()
#5 app\code\Magento\Backend\Block\Widget\Tabs.php(384): Magento\Framework\View\Element\AbstractBlock->toHtml()
#6 app\code\Magento\Catalog\view\adminhtml\templates\product\edit\tabs.phtml(101): Magento\Backend\Block\Widget\Tabs->getTabContent(Object(Magento\Downloadable\Block\Adminhtml\Catalog\Product\Edit\Tab\Downloadable))
#7 lib\internal\Magento\Framework\View\TemplateEngine\Php.php(59): include('C:\work\mage2.p...')
#8 lib\internal\Magento\Framework\View\Element\Template.php(255): Magento\Framework\View\TemplateEngine\Php->render(Object(Magento\Catalog\Block\Adminhtml\Product\Edit\Tabs), 'C:/work/mage2.p...', Array)
#9 lib\internal\Magento\Framework\View\Element\Template.php(275): Magento\Framework\View\Element\Template->fetchView('C:/work/mage2.p...')
#10 app\code\Magento\Backend\Block\Template.php(104): Magento\Framework\View\Element\Template->_toHtml()
#11 lib\internal\Magento\Framework\View\Element\AbstractBlock.php(652): Magento\Backend\Block\Template->_toHtml()
#12 lib\internal\Magento\Framework\View\Layout.php(521): Magento\Framework\View\Element\AbstractBlock->toHtml()
#13 lib\internal\Magento\Framework\View\Layout.php(497): Magento\Framework\View\Layout->_renderBlock('product_tabs')
#14 var\generation\Magento\Framework\View\Layout\Interceptor.php(206): Magento\Framework\View\Layout->renderNonCachedElement('product_tabs')
#15 lib\internal\Magento\Framework\View\Layout.php(473): Magento\Framework\View\Layout\Interceptor->renderNonCachedElement('product_tabs')
#16 var\generation\Magento\Framework\View\Layout\Interceptor.php(193): Magento\Framework\View\Layout->renderElement('product_tabs', true)
#17 lib\internal\Magento\Framework\View\Layout.php(548): Magento\Framework\View\Layout\Interceptor->renderElement('product_tabs')
#18 lib\internal\Magento\Framework\View\Layout.php(499): Magento\Framework\View\Layout->_renderContainer('left')
#19 var\generation\Magento\Framework\View\Layout\Interceptor.php(206): Magento\Framework\View\Layout->renderNonCachedElement('left')
#20 lib\internal\Magento\Framework\View\Layout.php(473): Magento\Framework\View\Layout\Interceptor->renderNonCachedElement('left')
#21 var\generation\Magento\Framework\View\Layout\Interceptor.php(193): Magento\Framework\View\Layout->renderElement('left', true)
#22 lib\internal\Magento\Framework\View\Layout.php(548): Magento\Framework\View\Layout\Interceptor->renderElement('left')
#23 lib\internal\Magento\Framework\View\Layout.php(499): Magento\Framework\View\Layout->_renderContainer('side.col')
#24 var\generation\Magento\Framework\View\Layout\Interceptor.php(206): Magento\Framework\View\Layout->renderNonCachedElement('side.col')
#25 lib\internal\Magento\Framework\View\Layout.php(473): Magento\Framework\View\Layout\Interceptor->renderNonCachedElement('side.col')
#26 var\generation\Magento\Framework\View\Layout\Interceptor.php(193): Magento\Framework\View\Layout->renderElement('side.col', true)
#27 lib\internal\Magento\Framework\View\Layout.php(548): Magento\Framework\View\Layout\Interceptor->renderElement('side.col')
#28 lib\internal\Magento\Framework\View\Layout.php(499): Magento\Framework\View\Layout->_renderContainer('page.main.conta...')
#29 var\generation\Magento\Framework\View\Layout\Interceptor.php(206): Magento\Framework\View\Layout->renderNonCachedElement('page.main.conta...')
#30 lib\internal\Magento\Framework\View\Layout.php(473): Magento\Framework\View\Layout\Interceptor->renderNonCachedElement('page.main.conta...')
#31 var\generation\Magento\Framework\View\Layout\Interceptor.php(193): Magento\Framework\View\Layout->renderElement('page.main.conta...', true)
#32 lib\internal\Magento\Framework\View\Layout.php(548): Magento\Framework\View\Layout\Interceptor->renderElement('page.main.conta...')
#33 lib\internal\Magento\Framework\View\Layout.php(499): Magento\Framework\View\Layout->_renderContainer('page.content')
#34 var\generation\Magento\Framework\View\Layout\Interceptor.php(206): Magento\Framework\View\Layout->renderNonCachedElement('page.content')
#35 lib\internal\Magento\Framework\View\Layout.php(473): Magento\Framework\View\Layout\Interceptor->renderNonCachedElement('page.content')
#36 var\generation\Magento\Framework\View\Layout\Interceptor.php(193): Magento\Framework\View\Layout->renderElement('page.content', true)
#37 lib\internal\Magento\Framework\View\Layout.php(548): Magento\Framework\View\Layout\Interceptor->renderElement('page.content')
#38 lib\internal\Magento\Framework\View\Layout.php(499): Magento\Framework\View\Layout->_renderContainer('page.wrapper')
#39 var\generation\Magento\Framework\View\Layout\Interceptor.php(206): Magento\Framework\View\Layout->renderNonCachedElement('page.wrapper')
#40 lib\internal\Magento\Framework\View\Layout.php(473): Magento\Framework\View\Layout\Interceptor->renderNonCachedElement('page.wrapper')
#41 var\generation\Magento\Framework\View\Layout\Interceptor.php(193): Magento\Framework\View\Layout->renderElement('page.wrapper', true)
#42 lib\internal\Magento\Framework\View\Layout.php(548): Magento\Framework\View\Layout\Interceptor->renderElement('page.wrapper')
#43 lib\internal\Magento\Framework\View\Layout.php(499): Magento\Framework\View\Layout->_renderContainer('backend.page')
#44 var\generation\Magento\Framework\View\Layout\Interceptor.php(206): Magento\Framework\View\Layout->renderNonCachedElement('backend.page')
#45 lib\internal\Magento\Framework\View\Layout.php(473): Magento\Framework\View\Layout\Interceptor->renderNonCachedElement('backend.page')
#46 var\generation\Magento\Framework\View\Layout\Interceptor.php(193): Magento\Framework\View\Layout->renderElement('backend.page', true)
#47 lib\internal\Magento\Framework\View\Layout.php(548): Magento\Framework\View\Layout\Interceptor->renderElement('backend.page')
#48 lib\internal\Magento\Framework\View\Layout.php(499): Magento\Framework\View\Layout->_renderContainer('root')
#49 var\generation\Magento\Framework\View\Layout\Interceptor.php(206): Magento\Framework\View\Layout->renderNonCachedElement('root')
#50 lib\internal\Magento\Framework\View\Layout.php(473): Magento\Framework\View\Layout\Interceptor->renderNonCachedElement('root')
#51 var\generation\Magento\Framework\View\Layout\Interceptor.php(193): Magento\Framework\View\Layout->renderElement('root', true)
#52 lib\internal\Magento\Framework\View\Layout.php(917): Magento\Framework\View\Layout\Interceptor->renderElement('root')
#53 var\generation\Magento\Framework\View\Layout\Interceptor.php(492): Magento\Framework\View\Layout->getOutput()
#54 lib\internal\Magento\Framework\View\Result\Page.php(241): Magento\Framework\View\Layout\Interceptor->getOutput()
#55 lib\internal\Magento\Framework\View\Result\Layout.php(162): Magento\Framework\View\Result\Page->render(Object(Magento\Framework\App\Response\Http\Interceptor))
#56 var\generation\Magento\Backend\Model\View\Result\Page\Interceptor.php(193): Magento\Framework\View\Result\Layout->renderResult(Object(Magento\Framework\App\Response\Http\Interceptor))
#57 lib\internal\Magento\Framework\App\Http.php(119): Magento\Backend\Model\View\Result\Page\Interceptor->renderResult(Object(Magento\Framework\App\Response\Http\Interceptor))
#58 lib\internal\Magento\Framework\App\Bootstrap.php(257): Magento\Framework\App\Http->launch()
#59 index.php(39): Magento\Framework\App\Bootstrap->run(Object(Magento\Framework\App\Http))

I fixed it with patch below.

/** @var string|null $attributeGroupCode */
$attributeGroupCode = $group->getAttributeGroupCode();
if (!$attributeGroupCode) {
	$group->setAttributeGroupCode($group->getId());
}

Place it at line 162 in app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tabs.php:

GitHub issue: 1646

Today I faced the bug again:

  1. I created a simple product using the default product template.
  • I created a new product template with a custom dropdown attribute.
  • I open the product, changed it template to the new and saved the product.
  • After saving the page is reloaded and here is the failure.

My fix still works.

I opened the eav_attribute_group table and saw that some records has null in attribute_group_code column:

SELECT * FROM  `eav_attribute_group` WHERE attribute_set_id IN (
	SELECT attribute_set_id FROM eav_attribute_set WHERE entity_type_id =4
)

This is because of the incorrect attribute group identifier generation algorithm:
Attribute group code generation algorithm is incorrect because it can return null for an attribute group with non-Latin name

A related bug: «Argument 1 passed to Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\ChildTab::setTab() must implement interface Magento\Backend\Block\Widget\Tab\TabInterface, instance of Magento\Framework\DataObject given» while opening a simple product in admin.