The «URL key for specified store already exists » message is throws in the following 2-3 places :
1. \Magento\UrlRewrite\Model\Storage\AbstractStorage::replace()
/**
* Create url rewrite object
*
* @param array $data
* @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite
*/
protected function createUrlRewrite($data)
{
$dataObject = $this->urlRewriteFactory->create();
$this->dataObjectHelper->populateWithArray(
$dataObject,
$data,
\Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class
);
return $dataObject;
}
2. \Magento\UrlRewrite\Model\Storage\DbStorage::insertMultiple()
/**
* Insert multiple
*
* @param array $data
* @return void
* @throws \Magento\Framework\Exception\AlreadyExistsException|\Exception
* @throws \Exception
*/
protected function insertMultiple($data)
{
try {
$this->connection->insertMultiple($this->resource->getTableName(self::TABLE_NAME), $data);
} catch (\Exception $e) {
if (($e->getCode() === self::ERROR_CODE_DUPLICATE_ENTRY)
&& preg_match('#SQLSTATE\[23000\]: [^:]+: 1062[^\d]#', $e->getMessage())
) {
throw new \Magento\Framework\Exception\AlreadyExistsException(
__('URL key for specified store already exists.'),
$e
);
}
throw $e;
}
}
3. \Magento\UrlRewrite\Model\Storage\DbStorage::doReplace()
It is only for the recent Magento 2.2 versions >= 2.2.0-RC1.4.
try {
$this->insertMultiple($data);
} catch (\Magento\Framework\Exception\AlreadyExistsException $e) {
/** @var \Magento\UrlRewrite\Service\V1\Data\UrlRewrite[] $urlConflicted */
$urlConflicted = [];
foreach ($urls as $url) {
$urlFound = $this->doFindOneByData(
[
UrlRewriteData::REQUEST_PATH => $url->getRequestPath(),
UrlRewriteData::STORE_ID => $url->getStoreId()
]
);
if (isset($urlFound[UrlRewriteData::URL_REWRITE_ID])) {
$urlConflicted[$urlFound[UrlRewriteData::URL_REWRITE_ID]] = $url->toArray();
}
}
if ($urlConflicted) {
throw new \Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException(
__('URL key for specified store already exists.'),
$e,
$e->getCode(),
$urlConflicted
);
} else {
throw $e->getPrevious() ?: $e;
}
}
How to diagnose it
The built-in exception handling of the all these 3 cases is low-quality : it just missed the original exception message and its backtrace.
So to diagnose the exception, log the original exception manually , or set a debugger breakpoint in these 3 code places.