Gutenberg: Crash occurs when state.preferences does not include insertUsage.

Created on 22 Mar 2019  路  16Comments  路  Source: WordPress/gutenberg

Describe the bug
If the preferences of a user saved in localstorage ( key WP_DATA_USER_${USER_ID} ) includes an empty object ( { 'core/editor': { preferences: {} } ) this will cause the entire editor to crash when the getInsertUsage selector is used. Which is automatically within several seconds after starting when getInserterItems is called.

I've run into this issue on two separate installations now running WordPress 5.1.1 without the Gutenberg plugin installed. I've not yet found what exactly is causing preferences to be saved as an empty object but I believe it's likely related to https://github.com/WordPress/gutenberg/blob/master/packages/data/src/plugins/persistence/index.js#L210 as insertUsage is explicitly omitted there.

To Reproduce
Steps to reproduce the behavior:

  1. Somehow get a localStorage where under WP_DATA_USER_${USER_ID} the core/editor object and then preferences object does not include an insertUsage key.
  2. Open Gutenberg.
  3. Wait several seconds or call wp.data.select( 'core/editor' ).getInserterItems()
  4. See error

Expected behavior
Gutenberg not to crash and invalid values in localStorage to either be fixed or discarded.

Screenshots
Screenshot 2019-03-22 at 16 09 44

Desktop (please complete the following information):

  • OS: iOS
  • Browser chrome
  • Version 72.0.3626.121
[Priority] High [Type] Bug

Most helpful comment

@MrCrin workaround would be to open the console and run localStorage.clear();.

All 16 comments

I think this only happens if you downgrade Gutenberg version.

Ideally, we mitigate this breakage and not load the preferences if they're not supported.

I think I've got the same problem!
The problem happens as soon as I click on the block inserter, or move the mouse out of the Title field.
If you click on the block inserter the problem happens immediately.

I believe the problem occurred because I deactivated Gutenberg.
I was running 5.3 but wanted to check which blocks were originally present in core and core-embed.

https://github.com/bobbingwide/oik-blocks/issues/28

Workaround

Reactivate Gutenberg

Easy steps to reproduce:

  1. Install and activate Gutenberg 5.3
  2. Use it to edit something
  3. Deactivate Gutenberg
  4. Choose Add new post
  5. Click on the block inserter icon.

Step 2. creates the local storage that WordPress core鈥檚 block editor can鈥檛 handle.

I think this only happens if you downgrade Gutenberg version.

@youknowriad As noted above since core includes a version of Gutenberg and the Gutenberg plugin includes a later version deactivating the plugin also causes the issue. I think that in many people's minds this will not translate to 'downgrading' Gutenberg even though it technically is.

So, is there a fix or a workaround? I am currently unable to use my website effectively? I'm not counting reactivating Gutenburg as an effective work around because is causes a different issue where edit.php doesn't load.

The only thing that work at the moment is to

  1. Deactivate gutenburg plugin to re-enable edit.php
  2. Open the post you want to edit
  3. Deactivate gutenburg plugin
  4. Refresh the page on the post you want to edit

Not really workable longterm...

I've also tried reinstalling latest Wordpress core after removing the plugin in the hope it might fix things but it didn't work.

I think I've got the same problem!
The problem happens as soon as I click on the block inserter, or move the mouse out of the Title field.
If you click on the block inserter the problem happens immediately.

I believe the problem occurred because I deactivated Gutenberg.
I was running 5.3 but wanted to check which blocks were originally present in core and core-embed.

bobbingwide/oik-blocks#28

Workaround

Reactivate Gutenberg

Do you have the issue with edit.php when it's reactivated?

@mrcrin Hi Michael, I've just network activated Classic-Editor 1.3 and now edit.php doesn't work.
Updating to 1.4 didn't resolve it.

@MrCrin workaround would be to open the console and run localStorage.clear();.

@MrCrin Hi Michael, I've just network activated Classic-Editor 1.3 and now edit.php doesn't work.
Updating to 1.4 didn't resolve it.

My comments about any effect that Classic-Editor made were due to invalid observations.

As @herregroen said, once it's broken, if reactivating Gutenberg does not resolve the issue, the only way to fix it is to clear local storage.

This is OK for people who have access to the console.
It's not a viable workaround on my iPad.
In my opinion, this is a problem with WordPress core.
Do you think this is something that can be fixed by the Health Check project?

One way of fixing is with this code:

diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js
index 03f029654..d6ecb4545 100644
--- a/packages/block-editor/src/store/selectors.js
+++ b/packages/block-editor/src/store/selectors.js
@@ -1115,7 +1115,7 @@ export const canInsertBlockType = createSelector(
  *                                            the number of inserts that have occurred.
  */
 function getInsertUsage( state, id ) {
-    return state.preferences.insertUsage[ id ] || null;
+    return get( state, [ 'preferences', 'insertUsage', id ], null );
 }

 /**
diff --git a/packages/data/src/plugins/persistence/index.js b/packages/data/src/plugins/persistence/index.js
index 4c9f5c7c0..649f95e8a 100644
--- a/packages/data/src/plugins/persistence/index.js
+++ b/packages/data/src/plugins/persistence/index.js
@@ -218,10 +218,6 @@ persistencePlugin.__unstableMigrate = ( pluginOptions ) => {
             },
         };

-        persistence.set( 'core/editor', {
-            ...coreEditorState,
-            preferences: omit( coreEditorState.preferences, [ 'insertUsage' ] ),
-        } );
         persistence.set( 'core/block-editor', blockEditorState );
     }
 };
diff --git a/packages/editor/src/store/defaults.js b/packages/editor/src/store/defaults.js
index d22d6e7f6..926494f23 100644
--- a/packages/editor/src/store/defaults.js
+++ b/packages/editor/src/store/defaults.js
@@ -4,6 +4,7 @@
 import { SETTINGS_DEFAULTS } from '@wordpress/block-editor';

 export const PREFERENCES_DEFAULTS = {
+    insertUsage: {},
     isPublishSidebarEnabled: true,
 };

diff --git a/packages/editor/src/store/test/reducer.js b/packages/editor/src/store/test/reducer.js
index f7be763e1..751563e77 100644
--- a/packages/editor/src/store/test/reducer.js
+++ b/packages/editor/src/store/test/reducer.js
@@ -481,6 +481,7 @@ describe( 'state', () => {
         it( 'should apply all defaults', () => {
             const state = preferences( undefined, {} );
             expect( state ).toEqual( {
+                insertUsage: {},
                 isPublishSidebarEnabled: true,
             } );
         } );

An alternative solution is a bit more complicated. It can be achieved by changing the key for local storage to fix the issue with Gutenberg downgrade:

Per @gziolo 's debugging, this happens as a result of preferences migration as part of the block editor module development in #13088, so is specifically an issue when running a version of Gutenberg v5.2.0 or newer, and then deactivating to WordPress v5.1.1 or older. Notably, the error will not occur when running WordPress v5.2.0-beta.1.

There's different levels of a fix depending on which combinations of versions we want to address. These are additive, and mostly reiterate what @gziolo shared above:

  1. We can remove the code which explicitly unsets the insertUsage property from core/editor preferences.

This would effectively be dead _data_ for newer versions of the editor, Gutenberg or WordPress v5.2.0 or newer. But it would prevent the error from occurring if the user deactivates or downgrades from these to an earlier version of WordPress.

Unfortunately, it will not resolve issues if a user had already used Gutenberg v5.2.0 or v5.3.0, then deactivated to WordPress v5.1.1 or older.

Important to note: The preferences migration has not landed in any stable version of WordPress (see data.js v5.1.1 vs. trunk). If this were implemented in time for WordPress v5.2.0, it may be fair to count this as a bug affecting only specific versions of the Gutenberg plugin.

  1. We can restore the preferences default for insertUsage in core/editor

This would effectively be dead _code_ for newer versions of the editor. This is slightly more undesirable than dead data, as it has more of a maintenance impact for future development.

  1. We can work to account for this in a backported patch release to WordPress v5.0.x and v5.1.x.

I expect this would be the least desirable option, considering backported patch releases to typically be reserved only for security fixes, not bug fixes.


To the general issue of how we arrived at this bug, I think it's important to consider:

  • Tolerance to unexpected preferences state. @gziolo 's first snippet of code above revises the implementation of getInsertUsage to use Lodash's _.get to try to retrieve a property at a given nested path, but not assume its existence. The error described by this issue occurs because we make an assumption in code that if core/editor preferences exist, then naturally insertUsage must exist and be an object. This is not a sustainable model for future development, at least without stronger guarantees of an object shape.
  • On the topic of guarantees, we should have better prescribed methods to handle changes in preferences shape. My attention was brought to a nearly identical issue experienced in WordPress.com's Calypso project, described at https://github.com/Automattic/wp-calypso/pull/3101 and related documentation. In their case, they used JSON Schema to describe an expected persisted object, as part of a mechanism to invalidate unexpected data automatically. A simpler (perhaps naive) alternative to this is incrementing a version number (or aligning to the WordPress release). The problem here is that discarding user preferences for each version change is not an acceptable user experience.

    • Given our current use of persisted data solely as part of preferences, a simple object, it may not be necessary at this time to explore this sort of invalidation. It may instead be enough to follow the guidance of the first point in simply being tolerant of preferences object shape not necessarily containing the properties we assume it does today as continuing to be true in the future.


In all, my personal recommendations would be, assuming at least the first item could be patched into the final release of WordPress v5.2.0:

  • Remove this code:

    • https://github.com/WordPress/gutenberg/blob/91b0c41e11719e6b985f61cdefe640dcdb805d49/packages/data/src/plugins/persistence/index.js#L221-L224

  • Find and update any reference to state.preferences.whatever, and ensure that use of the value of whatever would not produce an error if whatever is undefined.

    1. https://github.com/WordPress/gutenberg/blob/91b0c41e11719e6b985f61cdefe640dcdb805d49/packages/block-editor/src/store/selectors.js#L1118

    2. https://github.com/WordPress/gutenberg/blob/91b0c41e11719e6b985f61cdefe640dcdb805d49/packages/edit-post/src/store/selectors.js#L166

    3. https://github.com/WordPress/gutenberg/blob/91b0c41e11719e6b985f61cdefe640dcdb805d49/packages/nux/src/store/selectors.js#L58

    4. https://github.com/WordPress/gutenberg/blob/91b0c41e11719e6b985f61cdefe640dcdb805d49/packages/edit-post/src/store/selectors.js#L142

  • Remove this code:

Pull request: #14691

  • Find and update any reference to state.preferences.whatever, and ensure that use of the value of whatever would not produce an error if whatever is undefined.

Pull request: #14692

@aduth thanks for a detailed explanation. Can you give instructions that don鈥檛 involve running localStorage.clear() for resolving the issue in my iPad鈥檚 browsers.

thanks for a detailed explanation. Can you give instructions that don鈥檛 involve running localStorage.clear() for resolving the issue in my iPad鈥檚 browsers.

Other options aren't much better. You can use private mode in your browser or a different browser ntil next version of the plugin is released next week. Once the new version is shipped, the solution will be to install and activate it and disable again.

As explained in my previous comment. The fix from @aduth (#14691) has landed but won't be available until next plugin release.

Was this page helpful?
0 / 5 - 0 ratings