QuotaExceededError: Dom exception 22: An attempt was made to add something to storage that exceeded the quota

QuotaExceededError: Dom exception 22: An attempt was made to add something to storage that exceeded the quota

Using LocalStorage on iPhone with iOS 7 throws this error. I’ve been looking around for a resolvant, but considering I’m not even browsing in private, nothing is relevant.
I don’t understand why localStorage would be disabled by default in iOS 7, but it seems it is? I’ve tested on other websites as well, but with no luck. I even tried testing it using this website: http://arty.name/localstorage.html, but it doesn’t seem like it’s saving anything at all for some weird reason.
Has anyone had the same problem, only they’ve had luck fixing it? Should I switch my storage method?
I tried hard-debugging it by only storing a few lines of information, but to no avail. I used the standard localStorage.setItem() function to save.

Solutions/Answers:

Solution 1:

This can occur when Safari is in private mode browsing. While in private browsing, local storage is not available at all.

One solution is to warn the user that the app needs non-private mode to work.

Related:  Pass unknown number of arguments into javascript function

UPDATE: This has been fixed in Safari 11, so the behaviour is now aligned with other browsers.

Solution 2:

As mentioned in other answers, you’ll always get the QuotaExceededError in Safari Private Browser Mode on both iOS and OS X when localStorage.setItem (or sessionStorage.setItem) is called.

One solution is to do a try/catch or Modernizr check in each instance of using setItem.

However if you want a shim that simply globally stops this error being thrown, to prevent the rest of your JavaScript from breaking, you can use this:

https://gist.github.com/philfreo/68ea3cd980d72383c951

// Safari, in Private Browsing Mode, looks like it supports localStorage but all calls to setItem
// throw QuotaExceededError. We're going to detect this and just silently drop any calls to setItem
// to avoid the entire page breaking, without having to do a check at each usage of Storage.
if (typeof localStorage === 'object') {
    try {
        localStorage.setItem('localStorage', 1);
        localStorage.removeItem('localStorage');
    } catch (e) {
        Storage.prototype._setItem = Storage.prototype.setItem;
        Storage.prototype.setItem = function() {};
        alert('Your web browser does not support storing settings locally. In Safari, the most common cause of this is using "Private Browsing Mode". Some settings may not save or some features may not work properly for you.');
    }
}

Solution 3:

I use this simple function, which returns true or false, to test for localStorage availablity:

isLocalStorageNameSupported = function() {
    var testKey = 'test', storage = window.sessionStorage;
    try {
        storage.setItem(testKey, '1');
        storage.removeItem(testKey);
        return true;
    } catch (error) {
        return false;
    }
}

Now you can test for localStorage.setItem() availability before using it. Example:

if ( isLocalStorageNameSupported() ) {
    // can use localStorage.setItem('item','value')
} else {
    // can't use localStorage.setItem('item','value')
}

Solution 4:

I happened to run with the same issue in iOS 7 (with some devices no simulators).

Related:  Toggle HTML radio button by clicking its label

Looks like Safari in iOS 7 has a lower storage quota, which apparently is reached by having a long history log.

I guess the best practice will be to catch the exception.

The Modernizr project has an easy patch, you should try something similar: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js

Solution 5:

Here is an expanded solution based on DrewT’s answer above that uses cookies if localStorage is not available. It uses Mozilla’s docCookies library:

function localStorageGet( pKey ) {
    if( localStorageSupported() ) {
        return localStorage[pKey];
    } else {
        return docCookies.getItem( 'localstorage.'+pKey );
    }
}

function localStorageSet( pKey, pValue ) {
    if( localStorageSupported() ) {
        localStorage[pKey] = pValue;
    } else {
        docCookies.setItem( 'localstorage.'+pKey, pValue );
    }
}

// global to cache value
var gStorageSupported = undefined;
function localStorageSupported() {
    var testKey = 'test', storage = window.sessionStorage;
    if( gStorageSupported === undefined ) {
        try {
            storage.setItem(testKey, '1');
            storage.removeItem(testKey);
            gStorageSupported = true;
        } catch (error) {
            gStorageSupported = false;
        }
    }
    return gStorageSupported;
}

In your source, just use:

localStorageSet( 'foobar', 'yes' );
...
var foo = localStorageGet( 'foobar' );
...

Solution 6:

As already explained in other answers, when in Private Browsing mode Safari will always throw this exception when trying to save data with localStorage.setItem() .

To fix this I wrote a fake localStorage that mimics localStorage, both methods and events.

Related:  React.js: Example in tutorial not working

Fake localStorage: https://gist.github.com/engelfrost/fd707819658f72b42f55

This is probably not a good general solution to the problem. This was a good solution for my scenario, where the alternative would be major re-writes to an already existing application.