Is storage.sync preserved after extensions are updated

It would appear not to be the case, so I’d like to be sure!

Of course it it. It’s a persistent storage, same as “storage.local”, just synchronized to the cloud (once in a while).
I use it in several extensions so if it got cleared, it would be a huge issue for me.

Maybe you have initialization code that clears it?

1 Like

Do you have to sign in for storage sync to be persistent?

Nope, it’s just a normal storage that gets synced to the cloud if possible (for example on Android it behaves as a normal storage since it doesn’t support data synchronization just yet).

What exactly are you doing? If you are temporarily loading your addon, the data may get deleted… but I’m not sure how exactly. But during the normal addon update, nothing is lost for sure.

I’m in about:debugging and when I remove the extension to then load it again (to simulate an update), then the storage sync data is cleared, even if I’m signed in!!

CleanShot 2024-01-27 at 10.35.51@2x|690x256

Is this not the case on your end?

Removing extension clears all data :slight_smile:.
But that’s not how the update works.
There are other ways to simulate update, although I’m not sure how exactly. I think changing version in manifest and reloading extension will trigger the “runtime.onInstalled” callback with “update” reason. Or manually loading old version and then manually loading a newer version (without removing it first :slight_smile:).

1 Like

Reloading or manually loading an extension with or without changing the version will preserve the storage sync data, but I have to be sure that it correctly simulates an update to reliably debug.

I’ve just checked, increasing version in manifest file and reloading extension will fire the “onInstalled” event with “update” reason.

That should be enough to debug update behavior. Or what exactly are you trying to debug?

1 Like

My guess is that he wants the full upgrade experience:
Test permission requests > Observe or verify install time permission requests > Permission requests for extension upgrade


Thanks Hans! I was looking for that exact page and I couldn’t find it :smiley:.

I’m not interested in the permissions request (but it’s still good to know!), rather just testing if storage sync is persistent after an extension is updated. I’m trying to debug this issue reported by a user. Maybe I can use the information on this page. Thank you for pointing me in the right direction, Hans!

One thing I’m not sure of though is, if I load the extension version 4.7.6 from a zip file and then load the extension 4.7.7 from an updated zip file, do I properly simulate an update?

My findings by proceeding the above way show that storage sync is persistent, so I just need to know if proceeding this way is identical to an automatic update. It would make sense though, because I’m not changing the extension ID so the code should just get overwritten.

I did encounter a minor bug: occasionally the Preferences would disappear from the extension’s menu.

Visiting Themes and then coming back to Extensions would solve that issue.

@hans_squared Would you kindly please confirm that doing the above is equivalent to an automatic update. Sadly, I’m not able to test on Windows, so I don’t know if I’d get the same results on that OS.

I need to know because one of the users of my extension (who’s on Windows) has reported that storage sync is getting reset when the extension automatically updates:

I had actually reported this problem a very long time ago on Bugzilla, but I think it just got marked as RESOLVED and wasn’t investigated further!

The Firefox devs are too busy with so many other things (bugs / features / refactoring), they don’t go here unless really necessary :smiley:, so we have to help ourself if possible.

So let’s see, as I mentioned, the sync. storage is used by many extensions, even I use it in two of my extensions as a main storage, and I don’t remember a case when it would get cleared.

So I would say, it’s either something broken in the specific person Firefox and he should use Firefox reset to fix it, OR there is a bug in your code.

I’ve tried to look at your code now, but I can’t find any place where you actually “.set” any value to the sync storage, it’s always only “.get”:

1 Like

Thank you for clarifying, Juraj, as well as looking into my code. I have 2 functions in the background.js script that are involved in writing to storage.sync: they are setOptions and saveOptions.

Could you point me to a specific line where the “” happens?
I can’t find a single “.set(” string anywhere :slight_smile:.
I’m not used to search in github so maybe I’m doing something wrong :slight_smile:.

Please visit the following page

Then click inside the code and do CMD + F to launch the Find box. Type ‘storage.sync.set’ or search for setOptions or saveOptions. Something tells me that the last line in setOptions that returns a Promise or the syntax in saveOptions:

return{ options })

I’m not sure if I need to correct this as:

return{ options: options })

Thanks! I have no idea why the github search doesn’t show it.

Anyway, let’s analyze this code:

    /// Initialise options
    let options = {};
    let data = await => {
        if (logToConsole) {
            console.log('Failed to retrieve data from storage sync.');

    if (data.options) {
        options = data.options;
        delete data['options'];

    // If there are no options stored in storage sync or reset preferences is set, then use default options
    // Otherwise clear storage sync and only save options in storage sync
    if (isEmpty(options) || options.resetPreferences) {
        options = defaultOptions;
    } else {

I see two issues:

First, if the storage read fails (although highly unlikely), you will catch the error, so the code will continue executing and the “data” will be set to “undefined”.
Luckily it would then throw on the next line if (data.options), otherwise it would then continue and clear the storage.
If you don’t want your code to continue when unexpected fatal error happens, you should re-throw in the “.catch” handler or return a rejected promise from it.

Second issue - the storage is cleared whenever the “options” object is empty or the boolean property is set.
But why? Why clear it when the options object is empty?
When the user first installs it, the await will return empty object so “options” object will stay empty and the clear method will be called, correct?
And since you are modifying the options variable in the if condittion “options = data.options;”, the storage can be cleared also if the “data.options” object is empty (not undefined, just empty).

Although I can’t tell why would it clear upon addon update, but still, this code looks fairly dangerous to me :slight_smile:.

For clearing all data I would instead create a button that calls that function (and ideally, first call confirm in case user clicked it by accident).

1 Like

First issue >> not sure what code I need add and where? If data.options is falsy then options gets set to defaultOptions which is fine!

Second issue >> storage is cleared if options ISN’T empty! This is because the extension used to store search engines in storage sync in the beginning. Then storage limits were imposed so I moved the search engines to storage local. Now, storage sync should only contain options with no search engines.

I have a simple question btw: considering the following code:

const options = { 'a':1, 'b':2, 'c':3 };
await{ options });
const result = await'options');

What will result contain? Will it be:

{ 'options': { 'a':1, 'b':2, 'c':3 } }

How would you test this?

Oh, such a mistake :smiley:, I’m so sorry, I shouldn’t advice this late after work :slight_smile:.

Regarding the first issue, by throwing inside the “.catch” handler, the promise will be rejected and when you try to await it, it will throw. So I would throw right after the if statement that handles the logger.

Now about the question, the sync storage is a normal storage that gets synchronized to the cloud once in a while, so as long as you await the “set” operation, getting it back right away will give you the same value.
Unlike the “onChnaged” listeners that may(not) fire when the “set” promise is resolved: