(734)545-8017

The Human Element Blog

Magento 2 Bug Solution: URL key for specified store already exists

Posted May 24, 2017 by

Magento 2 Bug Fixes - URL Key for specified store already exists

Magento 2 bug fixes. Swatting the proverbial fly one tutorial at a time.

This article details a code fix for the Magento 2 bug where you see the error “URL Key for specified store already exists” when you save products and/or categories. There is also a downloadable module that you can use for your store.

**Please note that this module will not be compatible with versions of Magento 2.1.8 and above.

Download the HE UrlkeyRewrite Module

The Issue with url_rewrites in Magento 2

We recently helped a client out with a Magento 2.x bug that has been hanging out in the open issues on GitHub for awhile. We have seen this bug present itself in a variety of different ways. Tt seems mostly related to multi-store Magento 2 instances with products that have been either created via the API or the native product importer. The following error is thrown when trying to save categories on the products that were imported.

URL key for specified store already exists

You can find additional information on this issue on Stack Overflow and GitHub:
https://github.com/magento/magento2/issues/6671

If you are using the API to import products with a category on it, you may get an error that reads something simply like:

Bad Request

A workaround when using the API or importing products

To first get around that, try setting these two keys in your request:

1. url_key_create_redirect => '' 
2. save_rewrites_history => false

This should allow you to create products with categories. If you try to subsequently save the product, with or without additional categories, it will still throw the same error. This problem can be traced into the

vendor/magento/module-catalog-url-rewrite

module where the error actually presents itself.

The core of the problem

The actual problem is that Magento is incorrectly assigning the ‘store_id’ of the current store to url rewrites that are part of the original ‘Root Catalog’ category. This is the category with entity_id “1”. It was a little unclear why and when it was generating those urls and inserting them into the `url_rewrite` table, so we had to step through the code to find when these functions were getting called.

As it turned out, the magento-catalog-url-rewrite module has observers that kicked off this process based on these events.

catalog_category_prepare_save
catalog_category_save_after
catalog_product_import_bunch_save_after
catalog_product_import_bunch_delete_after
catalog_product_delete_before
catalog_product_save_before
catalog_product_save_after
catalog_category_save_before
catalog_category_move_after

MySQL throws errors related to an index requiring a unique key on store_id

Then after everything runs we get to the MySQL query which causes the database to throw an error. When the error is thrown, it stops the rest of the processes from running and returns the error specified above.

INSERT INTO `url_rewrite` 
     (`redirect_type`,`is_autogenerated`,`metadata`,`description`,`entity_type`,`entity_id`,`request_path`,`target_path`,`store_id`) 
     
VALUES 
     (0, 1, NULL, NULL, 'product', '6107', 'xyz.html', 'catalog/product/view/id/6107', '1'), 
     (0, 1, 'a:1:{s:11:"category_id";s:3:"345";}', NULL, 'product', '6107', 'temp-category-testing/xyz.html', 'catalog/product/view/id/6107/category/345', '1'), 
     (0, 1, 'a:1:{s:11:"category_id";s:1:"1";}', NULL, 'product', '6107', '/xyz.html', 'catalog/product/view/id/6107/category/1', '1'), 
     (0, 1, 'a:1:{s:11:"category_id";s:3:"305";}', NULL, 'product', '6107', '/xyz.html', 'catalog/product/view/id/6107/category/305', '1')

This query throws this error.

Duplicate entry ‘/xyz.html-1’ for key ‘URL_REWRITE_REQUEST_PATH_STORE_ID’

In particular, this was the value that seemed incorrect is this one:

(0, 1, ‘a:1:{s:11:”category_id”;s:1:”1″;}’, NULL, ‘product’, ‘6107’, ‘/xyz.html’, ‘catalog/product/view/id/6107/category/1’, ‘1’)

Category ID 1 is reserved for the Root Catalog and it was trying to apply the ‘store_id’ of the current scope store and not the ‘store_id’ of the default config, or “0”. In most multi-store Magento applications, it shouldn’t matter if this value is set to “0” because the category_id of the previous record for the store is really the only one that we care about.

Now that we understand what the problem is, we can back up in the code to find where it generated the $urls variable which it used in the end as the $bind parameters for the call to insert multiple records into the database. There are four methods that were called individually that handled all of this in the following file.

\Magento\CatalogUrlRewrite\Model\Product\AnchorUrlRewriteGenerator

The code fix that will eliminate this error when saving products and categories

Here is the proposed modification to the core file. This will take into account if one of the category ids is the Root Catalog. Before the foreach loop, set a current store variable to keep track of the store id that was passed in. If during the loop we find that we are dealing with the Root Catalog category id, set the store id to zero to prevent the duplicate url key issue. Subsequent trips through the loop would set back the original store id that was passed into the method. Making this modification allowed me to save the product with categories assigned to them.

public function generate($storeId, Product $product, ObjectRegistry $productCategories)
    {
        $urls = [];
        $currentStore = $storeId;
        foreach ($productCategories->getList() as $category) {
            $anchorCategoryIds = $category->getAnchorsAbove();
            if ($anchorCategoryIds) {
                foreach ($anchorCategoryIds as $anchorCategoryId) {
                    //Default: $anchorCategory = $this->categoryRepository->get($anchorCategoryId);
                    $storeId = ($anchorCategoryId === "1" ? "0" : $currentStore);

                    $anchorCategory = $this->categoryRepository->get($anchorCategoryId, $storeId);
                    $urls[] = $this->urlRewriteFactory->create()
                        ->setEntityType(ProductUrlRewriteGenerator::ENTITY_TYPE)
                        ->setEntityId($product->getId())
                        ->setRequestPath(
                            $this->urlPathGenerator->getUrlPathWithSuffix(
                                $product,
                                $storeId,
                                $anchorCategory
                            )
                        )
                        ->setTargetPath(
                            $this->urlPathGenerator->getCanonicalUrlPath(
                                $product,
                                $anchorCategory
                            )
                        )
                        ->setStoreId($storeId)
                        ->setMetadata(['category_id' => $anchorCategory->getId()]);
                }
            }
        }

        return $urls;
    }

Here is the resulting url_rewrite table view where you can see the record that has a store id of zero. It does not conflict now with the other rewrite for the current store. The default product ‘request_paths’ seem to be unaffected because the do not get stored with the leading forward slash.

url_rewrite_view

A downloadable package:

The attached downloadable package can be pasted into the app/code directory of your store and will apply the fix mentioned above. It contains two preferences and two method overrides. Once you have installed the module, make sure to flush all of the caches and run setup:di:compile. If this works for you, please comment and let me know!

Download the HE UrlkeyRewrite Module

Posted By

Director of Development

Comments (17)

Toni posted

Dear Paul, how can I install this package, just paste files or I must paste files and afther run ssh command: php bin/magento setup:upgrade. Best Regards Toni

    Paul Briscoe posted

    Hi Toni, You can paste the package into "/app/code/" and then make sure to run setup:di:compile and clear the Magento cache afterwards.

Aasim posted

Hi Paul, Thanks for detailed solution, but i'm still getting same issue while using your code.

    Paul Briscoe posted

    Hi Aasim, Can you give me more information about your store setup and M2 version number? Did you also re-rerun di compile and clear out the caches?

Tim posted

Hi Paul, I am having problems on one of our sites where this issue with the url_key is at the root of the issues, but I don't know for sure. I'm currently updated to 2.1.7 Is this module safe to try on this version to see if it fixes the problem?

    Paul Briscoe posted

    Hi Tim, I would suggest running it first on a development version of your site, but yes, the module should be safe to use on your version of Magento 2.1.7. Make a database backup before you install the script to be safe (or specifically, the url_rewrite table), but all the module does is update the storeId of the url rewrite that comes into conflict with the other URLs in your catalog. If you look at the 'url_rewrite' table before and after you should see the updates that get made. If you were to disable the module after testing and re-run the indexes, it would put it back into the state that it was before. Let me know if you have any issues.

Michael posted

We tried your fix on our Magento 2.1.6 installation but we're still getting the same issue after installing. We cleared all caches. Is there anything we can try?

    Paul Briscoe posted

    Hi there Michael, thanks for reaching out. Can you tell me a little bit more about your installation? What activities are you doing when you get the error? Do you have multiple websites? Do you have products with the same URL keys as categories? Can you verify by running module:status that the module is also active? You may have to run a setup:upgrade and setup:di:compile if you haven't. If you have tried all of those options, then what you need to do is look at the URL keys or your products and your categories for every store. Typically, what happens is that the URL key of the default(admin) store gets in the way when it tries to generate the paths during the save. Hope to hear back.

Michael posted

Hi Paul. We have one Magento website with four stores, where store ID 2 is the default store. We got this error when we save a product with a name that already exists or when we try to assign a existing product to a category. Honestly I don't now where to actually look at in our url_rewrite table. At least I can see that it seems that for the most of the products only a rewrite is generated for the default store, and for some of the products only for the other stores and not for the default. Is there anything you can do to put me in the right way?

    Paul Briscoe posted

    Sorry you are experiencing these issues, I know that it can be frustrating. I wanted to check again to see if you have run setup:upgrade and setup:di:compile from the command line of your Magento 2 webroot once the module is copied into app/code? Sometimes that can make a difference. If you have done that already, and you are still having issues, it sounds like there may be an issue with creating unique url rewrites by storeId as opposed to website. Without being able to look at the values in the database it is hard to tell exactly what is going on. If you wanted to test a solution, you could go into one of the affected products and change the url-key slightly in each store-view. Something like mystoreview-1, mystoreview-2, etc... and leave the main default config the same. After that if you have access to PHP MyAdmin, you can look at the 'url_rewrite' table filter by the entity_id of the product that you are working with. That is really going to be the only way that you will be able to see what the issue is at the system level. If you want to export that table and provide snippets of it I would be happy to take a look and see if there is anything that looks obvious. I also forgot ask what version of Magento 2 you are working on?

Paul C posted

Hey Paul, Great name btw. :) So I'm updating some products by way of csv files and when I try to import I get this - You need to specify the unique URL key manually in row Will this code fix issues with importing products via csv when the products are already in the DB? Thanks!

    Paul Briscoe posted

    Hey Paul :) Sorry for the delayed reply, but no, this will not fix that. In general, URL-Keys *should* be unique, especially if you are excluding categories from the path. Otherwise, Magento wouldn't be able to tell the difference between two products with the same URL key that were in different categories.

Rahul Bhandary posted

Hi, I had integrated the your module for solving the url key issue . After integration when I save product from admin panel . I am getting following error . Please let me know the solution for same . Warning: Declaration of HE\UrlkeyRewrite\Model\ProductUrlRewriteGenerator::generateForGlobalScope($productCategories) should be compatible with Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator::generateForGlobalScope($productCategories, $product = NULL, $rootCategoryId = NULL) in /Users/rahulbhandary/www/2xl/app/code/HE/UrlkeyRewrite/Model/ProductUrlRewriteGenerator.php on line 50

    Paul Briscoe posted

    Hello Rahul, Can you please let me know which version of Magento 2 you are using? In Magento 2.1.8 the method arguments have changed to include: "$productCategories, $product = null, $rootCategoryId = null" See: https://github.com/magento/magento2/blob/2.1.8/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteGenerator.php#L170 If you are using Magento 2.2.x version, we have not created a patch for that version yet. As the functionality of the newest version of this method is different, I would recommend that you do not use this module for that version and seek an alternate course of action.

Ricardo Dacosta posted

I got the following error when apply command di compile "[Exception] Warning: Declaration of HE\UrlkeyRewrite\Model\ProductUrlRewriteGenerator::generateForGlobalScope($productCategories) should be compatible with Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator::generateForGlobalScope($produc tCategories, $product = NULL, $rootCategoryId = NULL) in /home/145843.cloudwaysapps.com/wxjvustdeu/public_html/app/co de/HE/UrlkeyRewrite/Model/ProductUrlRewriteGenerator.php on line 50 " Magento 2.2.2

    Paul Briscoe posted

    Hello Ricardo, We have not yet created a patch for this on Magento version 2.2.x. You are getting this error because the method arguments have changed for this function starting at version 2.1.8. To remove the module, you must go into your codebase and first disable the module by running: `php bin/magento module:disable HE_UrlkeyRewrite` After this, you should be able to delete the module by removing it from inside of app/code/. The module code itself does not do anything destructive, but because of the later version of Magento2 that you are using, once it is disabled or removed, you may need to reindex your products and categories by running: `php bin/magento indexer:reindex` Please let me know if you need any more assistance. Thanks!

Ricardo posted

Thank you Paul! All is well now. Do you have plans to update the code for it to work on 2.2.x? I tried to modify it just a bit to not give me errors but it didnt do the magic to remove my -1 suffix on product pages.

Post a comment