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

9

228

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.

Fassett answered 16/1, 2014 at 10:35 Comment(7)
It usually means you tried to store something with a size that exceeded available storage space. What browser are you using (Safari, Chrome, etc.)? Can you share a little more of the code you have been using and if possible the data you're trying to store.Amused
This should be considered as a bug or issue on the Safari side. It does not make sense that you can not use localStorage in incognito mode...Neighborhood
Use a feature detect that tests for this specific issue. If storage is not available, consider shimming localStorage with memoryStorage. disclaimer: I am the author of the linked packagesCollected
Similar question with great solutions: #35608224Mckenna
In April 2017 a patch was merged into Safari, so it aligned with the other browsers. Will likely land in Safari 11. bugs.webkit.org/show_bug.cgi?id=157010Khorma
I can confirm this has been fixed in Safari iOS 11. Tested Private browsing + sessionStorage.setItem() then sessionStorage.getItem() successfully on iPhone6 and iPhone8.Polygon
This also happens when you run into the regular ~10MB size limit of localStorage Another test site is storage-quota.glitch.mePhonon
N
377

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.

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

Nutter answered 4/5, 2014 at 7:48 Comment(9)
Your post was just incredibly helpful and timely for me today (less than 24 hours later). For reference, here's how to turn on/off private browsing: imore.com/how-use-private-browsing-ios-7-safariApthorp
+1 fixed my issue. I was checking for the existence of LocalStorage (if( typeof Storage != 'undefined' ) { ... }) before trying to load and save information but getting this error. Turns out Storage is still defined even when it's unusable. Using try/catch from now on whenever I use LocalStorage.Tipton
Thanks! Weird error by safari. Should have been more informative. :DExtensile
I fixed my issue using the following: <gist.github.com/engelfrost/fd707819658f72b42f55>?Riha
This looks like a good idea, yet, looking at the code, it seems that the FakeLocalStorage is not persistent and would give your users a sense of false security that their data is stored. If you replace it with cookies the cookies themselves would be deleted. So this is a no-no as well.Nutter
A fix may be incoming as of Safari Tech Preview 29: "Fixed QuotaExceededError when saving to localStorage in private browsing mode or WebDriver sessions". See developer.apple.com/safari/technology-preview/release-notesLekishalela
This can also occur if storage limit is reached which can be easily done by saving images for example.Hertzfeld
It has been fixed in iOS 11Plumbum
this has just been triggered on an iPhone with iOS 10.3.3 on one of my public projects... we are in 2018 now.. wtf apple.. even android on ice cream sandwitch would get the latest chrome version...Crier
T
108

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.');
    }
}
Tay answered 22/11, 2014 at 19:16 Comment(2)
Why add the setItem to the Storage object if you wont be able to use it anyway?Aculeate
The point of my snippet is to simply ignore JS errors from being thrown if you want your app to not be totally broken in Safari private mode.Tay
C
16

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')
}
Crape answered 11/5, 2015 at 18:1 Comment(4)
Have I missed something? Why is window.sessionStorage been used instead of window.localStorage for a method called isLocalStorageNameSupported?Deathbed
@lthar - see the documentation here: w3schools.com/html/html5_webstorage.asp Most importantly this part: HTML local storage provides two objects for storing data on the client: window.localStorage - stores data with no expiration date window.sessionStorage - stores data for one session (data is lost when the browser tab is closed)Crape
@DrewT, but what's the difference in this situation if you remove your test key? It doesn't matter where I will store my test key if I am going to delete it. Am I wrong? Why is the session storage better that the local one?Antiphonary
@TurakVladyslav you're right there is really no difference here except that using sessionStorage makes it more manageable for setting breakpoints if you want to test your development. There is no true argument for which is "better" and it's really just a personal preference here that errs on the side of caution. The main thing to note is that both sessionStorage and localStorage are both implementations of the HTML5 webstorage API.Crape
N
5

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

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

Noland answered 13/3, 2014 at 0:30 Comment(0)
C
3

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' );
...
Checkers answered 8/7, 2015 at 17:11 Comment(0)
T
2

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.

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.

Ten answered 18/3, 2015 at 14:20 Comment(2)
What exactly does it fix? It doesn't persist anything, so what's the point?Uchida
It "fixes" Safari when in private browsing mode. (This is not clear in my answer, thanks for pointing that out. I'll edit my answer). Nothing is supposed to be persisted when in private browsing mode regardless, so not persisting is not a relevant issue here. What this fixed for me was to allow users to run an already existing application, without major re-writes, even when in Private Browsing mode in Safari.Ten
P
2

Update (2016-11-01)

I was using AmplifyJS mentioned below to work around this issue. However, for Safari in Private browsing, it was falling back to a memory-based storage. In my case, it was not appropriate because it means the storage is cleared on refresh, even if the user is still in private browsing.

Also, I have noticed a number of users who are always browsing in Private mode on iOS Safari. For that reason, a better fallback for Safari is to use cookies (if available). By default, cookies are still accessible even in private browsing. Of course, they are cleared when exiting the private browsing, but they are not cleared on refresh.

I found the local-storage-fallback library. From the documentation:

Purpose

With browser settings like "Private Browsing" it has become a problem to rely on a working window.localStorage, even in newer browsers. Even though it may exist, it will throw exceptions when trying to use setItem or getItem. This module will run appropriate checks to see what browser storage mechanism might be available, and then expose it. It uses the same API as localStorage so it should work as a drop-in replacement in most cases.

Beware of the gotchas:

  • CookieStorage has storage limits. Be careful here.
  • MemoryStorage will not persist between page loads. This is more or less a stop-gap to prevent page crashes, but may be sufficient for websites that don't do full page loads.

TL;DR:

Use local-storage-fallback (unified API with .getItem(prop) and .setItem(prop, val)):

Check and use appropriate storage adapter for browser (localStorage, sessionStorage, cookies, memory)

Original answer

To add upon previous answers, one possible workaround would be to change the storage method. There are a few librairies such as AmplifyJS and PersistJS which can help. Both libs allow persistent client-side storage through several backends.

For AmplifyJS

localStorage

  • IE 8+
  • Firefox 3.5+
  • Safari 4+
  • Chrome
  • Opera 10.5+
  • iPhone 2+
  • Android 2+

sessionStorage

  • IE 8+
  • Firefox 2+
  • Safari 4+
  • Chrome
  • Opera 10.5+
  • iPhone 2+
  • Android 2+

globalStorage

  • Firefox 2+

userData

  • IE 5 - 7
  • userData exists in newer versions of IE as well, but due to quirks in IE 9's implementation, we don't register userData if localStorage is supported.

memory

  • An in-memory store is provided as a fallback if none of the other storage types are available.

For PersistentJS

  • flash: Flash 8 persistent storage.
  • gears: Google Gears-based persistent storage.
  • localstorage: HTML5 draft storage.
  • globalstorage: HTML5 draft storage (old spec).
  • ie: Internet Explorer userdata behaviors.
  • cookie: Cookie-based persistent storage.

They offer an abstraction layer so you don't have to worry about choosing the storage type. Keep in mind there might be some limitations (such as size limits) depending on the storage type though. Right now, I am using AmplifyJS, but I still have to do some more testing on iOS 7/Safari/etc. to see if it actually solves the problem.

Pretoria answered 30/6, 2015 at 18:7 Comment(1)
Editor John: I realize you and Jonathan Alzetta are probably the same account and you're just trying to improve your answer, but if so you should really log-in as Jonathan Alzetta and edit this answer, and then it won't go through the review queue. Recover your account if you need to.Wertheimer
K
1

In April 2017 a patch was merged into Safari, so it aligned with the other browsers. This was released with Safari 11.

https://bugs.webkit.org/show_bug.cgi?id=157010

Khorma answered 2/8, 2017 at 15:0 Comment(0)
S
0

This question and answer helped me solve a specific problem with signing up new users in Parse.

Because the signUp( attrs, options ) function uses local storage to persist the session, if a user is in private browsing mode it throws the "QuotaExceededError: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota." exception and the success/error functions are never called.

In my case, because the error function is never called it initially appeared to be an issue with firing the click event on the submit or the redirect defined on success of sign up.

Including a warning for users resolved the issue.

Parse Javascript SDK Reference https://parse.com/docs/js/api/classes/Parse.User.html#methods_signUp

Signs up a new user with a username (or email) and password. This will create a new Parse.User on the server, and also persist the session in localStorage so that you can access the user using {@link #current}.

Senate answered 18/9, 2015 at 6:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.