null is not an object (evaluating 'delete i[s[u]]')

 

Magento 2: Error


Description:

 

Error Signature: jquery.storageapi.min.js:null is not an object (*)

Error Message: null is not an object (evaluating 'delete i[s[u]]')

 





Issue Discovery:

This is a well known issue which occurs on Magento 2. The root cause of this error as it stands today seems to be a problem with local storage and how we are using, and manipulating data on the site. I am able to replicate the error on a Safari browser on site when I clear the local storage then click on things. More specfically in the console typing: localStorage.clear() then hitting a magento element.
We can find the piece of code which is throwing the error:

 

 


    
                    function i(t) {
                        var r, i, n = arguments.length,
                            o = window[t],
                            s = arguments,
                            a = s[1];
                        if (2 > n) throw Error("Minimum 2 arguments must be given");
                        if (e.isArray(a)) {
                            for (var u in a) o.removeItem(a[u]);
                            return !0
                        }
                        if (2 == n) return o.removeItem(a), !0;
                        try {
                            r = i = JSON.parse(o.getItem(a))
                        } catch (f) {
                            throw new ReferenceError(a + " is not defined in this storage")
                        }
                        for (var u = 2; n - 1 > u; u++)
                            if (i = i[s[u]], void 0 === i) throw new ReferenceError([].slice.call(s, 1, u).join(".") + " is not defined in this storage");
                        if (e.isArray(s[u]))
                            for (var c in s[u]) delete i[s[u][c]];
                        else delete i[s[u]];
    
                



More specifically with further digging I was able to find the exact variable which throws the issue inside localStorage. Clearing out variable "mage-cache-storage" will throw this error ie the command: localStorage.removeItem("mage-cache-storage"). This is useful context because this is the root cause of this error and we can come up with a fix.

We can now take a look to see how "mage-cache-storage" affects the code itself and why this is thrown in the code. Looking deeper we can use the command JSON.parse(localStorage.getItem("mage-cache-storage")) to see what is inside mage-cache-storage.

Inside this object we can see a lot of variables which define elements of the page. Which makes sense why when it is deleted we get so many issues in the platform.

It seems like a simple solution to this would be can we just check if this is empty and reinitialize this data. Looking around our /Magento_Customer/js/customer-data.min.js file we can see a few functions of interest. The first and possibly most pragmatic is this piece of code:

 

 


                    

                customerData = {
                    init: function() {
                        var expiredSectionNames = this.getExpiredSectionNames();
                        if (expiredSectionNames.length > 0) {
                            _.each(dataProvider.getFromStorage(storage.keys()), function(sectionData, sectionName) {
                                buffer.notify(sectionName, sectionData);
                            });
                            this.reload(expiredSectionNames, false);
                        } else {
                            _.each(dataProvider.getFromStorage(storage.keys()), function(sectionData, sectionName) {
                                buffer.notify(sectionName, sectionData);
                            });
                            if (!_.isEmpty(storageInvalidation.keys())) {
                                this.reload(storageInvalidation.keys(), false);
                            }
                        }
                        if (!_.isEmpty($.cookieStorage.get('section_data_clean'))) {
                            this.reload(sectionConfig.getSectionNames(), true);
                            $.cookieStorage.set('section_data_clean', '');
                        }
                    },
                    initStorage: function() {
                        $.cookieStorage.setConf({
                            path: '/',
                            expires: new Date(Date.now() + parseInt(options.cookieLifeTime, 10) * 1000)
                        });
                        storage = $.initNamespaceStorage('mage-cache-storage').localStorage;
                        storageInvalidation = $.initNamespaceStorage('mage-cache-storage-section-invalidation').localStorage;
                    },
                

 

 

Here we can see that customerData.initStorage() will initialize a lot of our local storage variables. This is a potential solution as we can see when this code is run we are running a few different functions which could cause failures if not checked if they are empty.





Potential fixes:

To solve this the first thing we can do to make sure things aren't failing is check before we do a deletion or removal to see if the data exists. If the data does not exist we would want to reinitilize it. The code we would update would look something along the lines of:

 

 


                    

                    remove: function(sections) {
                        if (typeof storage === 'undefined') {
                            customerData.initStorage();
                        }
                        _.each(sections, function(sectionName) {
                            storage.remove(sectionName);
                            if (!sectionConfig.isClientSideSection(sectionName)) {
                                storageInvalidation.set(sectionName, true);
                            }
                        });
                    }

                    
                

 

 

Here we are adding a check to see if storage is undefined and then initializing our variables if it is. This should fix a lot of the problem as if data is going to be deleted and it does not exist we can check if it exist first.

The other problem we now have is if we need to do anything else like updating data or performing any other action we need to know if "mage-cache-storage" is defined or not. We would need to do some check for this as well however doing a blanket reinitialization might not be subtle enough to prevent other errors from occurring. We can investigate further to find a root fix.

The next thing which to look at is the file which actually throws the error. Here we have the file /jquery/jquery.storageapi.min.js. Here we can take a look at it's remove function as well:

 

 



                    
                    remove: function() {
                        if (arguments.length < 1) throw Error("Minimum 1 argument must be given");
                        return this._callMethod(i, arguments)
                    },

                

 

 

Here we can see that we also have a remove function. This function has a check which looks to see if we have anything passed in as an element to remove. This isn't greatly useful for us though because even if we pass an undefined variable into the input of this function it won't throw an error. The argument will still be passed into the function along with our variable i.

Here I think a fix might be to run the variable t(t) to reinitialize our customer data and allow our user to use the page. The following is t(t) which is used to reinitialize the necessary variables:

 

 




                    function t(t) {
                        var r, i, n, o = arguments.length,
                            s = window[t],
                            a = arguments,
                            u = a[1];
                        if (2 > o) throw Error("Minimum 2 arguments must be given");
                        if (e.isArray(u)) {
                            i = {};
                            for (var f in u) {
                                r = u[f];
                                try {
                                    i[r] = JSON.parse(s.getItem(r))
                                } catch (c) {
                                    i[r] = s.getItem(r)
                                }
                            }
                            return i
                        }
                        if (2 != o) {
                            try {
                                i = JSON.parse(s.getItem(u))
                            } catch (c) {
                                throw new ReferenceError(u + " is not defined in this storage")
                            }
                            for (var f = 2; o - 1 > f; f++)
                                if (i = i[a[f]], void 0 === i) throw new ReferenceError([].slice.call(a, 1, f + 1).join(".") + " is not defined in this storage");
                            if (e.isArray(a[f])) {
                                n = i, i = {};
                                for (var m in a[f]) i[a[f][m]] = n[a[f][m]];
                                return i
                            }
                            return i[a[f]]
                        }
                        try {
                            return JSON.parse(s.getItem(u))
                        } catch (c) {
                            return s.getItem(u)
                        }
                    }


                    

 

 

Another potential fix for this is to check if i is null before we run our function and then run a refresh of the page. This is not an ideal solution but it is a quick catch in the case that data is missing. From a user experience perspective I would probably let them know there was an error on the page before reloading but that is at your discretion. The solution here would look like:

 

 



                    if (2 > n) throw Error("Minimum 2 arguments must be given");
                    if (e.isArray(a)) {
                        for (var u in a) o.removeItem(a[u]);
                        return !0
                    }
                    if (2 == n) return o.removeItem(a), !0;
                    try {
                        r = i = JSON.parse(o.getItem(a))
                    } catch (f) {
                        throw new ReferenceError(a + " is not defined in this storage")
                    }
                    for (var u = 2; n - 1 > u; u++)
                        if (i = i[s[u]], void 0 === i) throw new ReferenceError([].slice.call(s, 1, u).join(".") + " is not defined in this storage");
                    if (e.isArray(s[u]))
                        for (var c in s[u]) delete i[s[u][c]];
                    else {
                        if (i==null){
                            
                            window.location.reload(false); 
                        }   
                        
                        else{

                            try {
                                delete i[s[u]];
                            } 

                            catch(e){ 
                                console.error("storage error here: " + e ) // Noibu will catch this error if it does occur in the future.
                            }
                        }
                        
                    }


                

 

Fixing this depends on how and what this code is doing in a larger context. I don't know as I did not design it however, it seems like it is to do with front end manipulation of elements. This means upon failure it would have significant impact on navigation through pages.

 

Was this article helpful?
0 out of 0 found this helpful
Have more questions? Submit a request

Comments

0 comments

Please sign in to leave a comment.