1

I client of mine recently upgraded a very basic WordPress install to version 5.6.1. While the update seemed to go well, right at the very end the site died with an error that read something like this; identifying details altered for privacy:

[Sat Feb 06 12:12:11.123456 2021] [fcgid:warn] [pid 128] [client 12.34.56.78:12345] mod_fcgid: stderr: PHP Fatal error:  Uncaught Error: Class 'WP_Site_Health' not found in /var/www/html/wp-includes/rest-api.php:321

I have fairly deep experience debugging WordPress and related PHP sites, but this was baffling. According to them they disabled all of the plug-ins and themes and even downloaded a clean archive of WordPress 5.6.1 and did the basic manual upgrade process of retaining the wp-config.php, .htaccess and related user specific uploads in the wp-content directory and the error still shows up.

I’ve read advice like what was posted here on the WordPress forums that basically state one should restore from a backup and try again, but find that advice to be a classic “Hail Mary” play; sometimes it works and other times you are just left in the same exact position as before but with new file modification dates.

So given that the class WP_Site_Health and the file wp-includes/rest-api.php are both items that exist in core WordPress, what can be done to quickly patch the system to get it working again?


P.S.: And to truly confirm the files were fine, I did the following:

  • Created a Tar/Gzip archive of the whole WordPress install — including all wp-content items — and downloaded it to my desktop.
  • Then I decompressed the archive, went into that directory and ran git init inside of it to create a Git repo.
  • I left the site as-is as the master branch and then created a new branch called test.
  • In that test branch, I manually downloaded a 100% WordPress 5.6.1 install from WordPress directly, manually copied the clean files into the new branch and committed them.
  • Then I ran local git diff master..test to see what files were changed between the source master branch and the test branch. The diff said the files from the server versus clean WordPress 5.6.1 files were 100% the same; what was on the server is 100% the same as a clean WordPress install.
4
  • This is a classic symptom of a failed or incomplete update. The correct solution is to perform a manual update: wordpress.org/support/article/updating-wordpress/#manual-update Feb 7 at 6:38
  • @JacobPeattie And if you actually read what I posted I stated “According to them they disabled all of the plug-ins and themes and even downloaded a clean archive of WordPress 5.6.1 and did the basic manual upgrade process of retaining the wp-config.php, .htaccess and related user specific uploads in the wp-content directory and the error still shows up.” Feb 7 at 6:43
  • "According to them". You need to verify that it was done properly. Feb 7 at 6:48
  • @JacobPeattie Just updated my question to explain how I just confirmed the files were 100% the same: I downloaded the site as is, inited a Git repo in that directory, created a new branch, then manually added the files from a clean WordPress 5.6.1 and ran a basic git diff between the branches. The results? 100% the same… Expect for now the only file changed is the one I indicated in the answer: wp-includes/rest-api.php. So your advice is well intentioned — mucking with core files is not ideal — but honestly there was no difference between the server files and clean WordPress 5.6.1. Feb 7 at 7:27
1

Have a look at what point in your code you're making calls to the WordPress REST API or any other code which makes use of WP_Site_Health.

If you're making calls in your <theme>/functions.php file, for example, it won't work because functions.php is included before class WP_Site_Health in wp-settings.php.

See wp-settings.php code (WordPress 5.7.2):

// Load the functions for the active theme, for both parent and child theme if applicable.
foreach ( wp_get_active_and_valid_themes() as $theme ) {
    if ( file_exists( $theme . '/functions.php' ) ) {
        include $theme . '/functions.php';
    }
}
unset( $theme );

/**
 * Fires after the theme is loaded.
 *
 * @since 3.0.0
 */
do_action( 'after_setup_theme' );

// Create an instance of WP_Site_Health so that Cron events may fire.
if ( ! class_exists( 'WP_Site_Health' ) ) {
    require_once ABSPATH . 'wp-admin/includes/class-wp-site-health.php';
}
WP_Site_Health::get_instance();

Despite the lack of elegance, one thing you can do about it is to ensure the class is loaded by copying part of the code from wp-settings.php and pasting it before your code. For example, in your <theme>/functions.php, you can do this:

if ( ! class_exists( 'WP_Site_Health' ) ) {
    require_once ABSPATH . 'wp-admin/includes/class-wp-site-health.php';
}
WP_Site_Health::get_instance();

---8<---
code depending on WP_Site_Health
---8<---
3
  • 1
    Thanks for the new post! But honestly, what is the difference between your if ( ! class_exists( 'WP_Site_Health' ) ) { and my solution doing the exact same way? Anyway, I finally had time to go through the theme and figure out where the problematic call was in the hacked together theme we were dealing with. Fixed the issue and now the site has clean core WordPress code and the theme is a (working) mess of its own. Jul 1 at 20:49
  • 1
    The difference is in where you're adding the if. functions.php is supposed to contain custom code. Modifying wp-includes/rest-api.php is really not what you want as it is wordpress native code. Also beware there's a huge chance you're doing something out of specs (like calling register_rest_route() out of the rest_api_init hook, for example. See developer.wordpress.org/rest-api/extending-the-rest-api/…) to get this error. Jul 1 at 21:19
  • 1
    “Also beware there's a huge chance you're doing something out of specs…” Not me! But whatever transient developer hacked together the theme that site used. Anyway, without going into ugly specifics, the issue was tracked down, isolated to the theme and all fixed now. We can now update WordPress core on the site without breaking the whole thing. Jul 1 at 22:06
-1

The solution is to add a PHP class_exists check around line 321 in wp-includes/rest-api.php.

While patching WordPress core like this makes me wince, the check I added just confirms if WP_Site_Health exists in the code WordPress loaded to render itself. Here is what exists around line 321 in core WordPress code for wp-includes/rest-api.php:

// Site Health.
$site_health = WP_Site_Health::get_instance();
$controller  = new WP_REST_Site_Health_Controller( $site_health );
$controller->register_routes();

Here is what I adjusted around there to get the site back up and running:

// Site Health.
if ( ! class_exists( 'WP_Site_Health' ) ) {
    require_once ABSPATH . 'wp-admin/includes/class-wp-site-health.php';
}
$site_health = WP_Site_Health::get_instance();
$controller  = new WP_REST_Site_Health_Controller( $site_health );
$controller->register_routes();

The logic is pretty simple: Using the PHP function class_exists, the code simply checks WP_Site_Health exists or not. If it doesn’t exist, then WordPress loads it using require_once and moves on. If it does exists? The condition fails that’s that.

It still is baffling why — if you look at wp-settings.php which bootstraps all of the classes when WordPress is loaded — rest-api.php loads around line 240, but makes a call to WP_Site_Health around line 539. Why would rest-api.php — which is loaded first — making a call to WP_Site_Health which only loads waaay later on in the require list?

Anyway, this is not pretty but it works and should be harmless to add to quickly get a site up and running again.

1
  • There’s no reason you should ever do this. All you’re doing is covering up a deeper issue. Feb 7 at 6:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.