Automattic / wp-calypso

The JavaScript and API powered WordPress.com
https://developer.wordpress.com
GNU General Public License v2.0
12.39k stars 1.98k forks source link

[Bug][sentry][wp-includes/heartbeat.js]: `Cannot read properties of null (reading 'hooks')` #67242

Open fullofcaffeine opened 1 year ago

fullofcaffeine commented 1 year ago

Quick summary

I'm investigating this Sentry issue, it happens in wp-includes/js/heartbeat.js, line 469 (search in opengrok) because wp is null for some reason, so I assume the @wordpress/hooks package is not being enqueued/loaded when this script runs, sometimes, for some reason.

Maybe a network race condition or the necessary ]wp library is not being enqueued in the context the error happens?

Steps to reproduce

Couldn't reproduce manually, but see the Sentry issue for more info:

https://sentry.io/organizations/a8c/issues/2538445063/?project=5876245

Unfortunately, the stacktrace points to a minified/concatenated file, but I did the reverse-engineering and found out it originates in this line: wp-includes/js/heartbeat.js, line 469 (search in opengrok).

See also this thread on Slack for further context: p1661902530227569-slack-C7YPUHBB2

What you expected to happen

No exception is thrown, wp is not undefined in wp-includes/js/heartbeat.js, line 469 (search in opengrok).

What actually happened

wp is undefined when the call to wp.hooks.doAction happens, wp-includes/js/heartbeat.js, line 469 (search in opengrok).

Browser

Google Chrome/Chromium

Context

No response

Platform (Simple, Atomic, or both?)

Simple

Other notes

No response

Reproducibility

Intermittent

Severity

Some (< 50%)

Available workarounds?

No but the platform is still usable

Workaround details

No response

jsnajdr commented 1 year ago

This looks like an instance of a bug described here: https://core.trac.wordpress.org/ticket/55628#comment:15

The heartbeat script declares its dependencies as ( 'jquery', 'wp-hooks' ), so wp.hooks should be there. But because of the mentioned bug, it isn't there -- wp-hooks is loaded only after heartbeat, although the dependency graph tells it to load before.

The heartbeat script is in WordPress' "default" script directory, meaning it's shipped with WordPress, as opposed to coming from a plugin. So is the wp-hooks script. Except when the Gutenberg plugin is active, which registers its own version of wp-hooks, this time in a plugin directory.

And when a "default" script depends on a "plugin" script, then a buggy concatenation code (load-scripts.php) can change the order. It will concatenate the heartbeat script, causing it to load first, and will not concatenate the wp-hooks script (because it's coming from a plugin).

There's no straightforward fix for this as it's a bug in the wp_print_scripts code. Nobody else does anything wrong. But one workaround is to pretend that the heartbeat script has translations:

wp_set_script_translations( 'heartbeat' );

Then it will be excluded from the load-scripts.php concatenation and the load order with wp-hooks won't be reversed.