Guide

WordPress fatal error — how to find what crashed your site

You hit your homepage and get a white screen, a generic "There has been a critical error on this website" message, or a hard HTTP 500. Admin is locked out the same way. You refresh — same thing.

1. Problem

Your WordPress site was working an hour ago. Now it is a blank white page, or a terse There has been a critical error on this website, or a raw stack trace dumped straight to the browser. You did not deploy anything. Nobody edited a file. Yet the runtime stopped mid-request, and the only thing you have to go on is whatever PHP managed to write before it died.

If you searched "wordpress fatal error how to fix" or "wordpress critical error no admin access", the top results tell you to enable WP_DEBUG, rename your plugins folder, and switch to a default theme. That advice eventually works, but it is guesswork: you are disabling things at random until the white screen goes away, with no idea which change actually fixed it or whether it will come back. The real question is narrower and answerable: which file, on which request, raised the fatal, and what put that code in the path.

A PHP fatal surfaces as the wp_php_fatals_total signal. This guide walks through finding the exact fatal in the logs, separating the cause from the symptom, and fixing it so it does not silently recur. If your fatal appeared right after an update, the companion guide on the warning-to-update-to-fatal chain goes deeper on tracing the trigger.

2. Impact

A fatal is not a slow page or a cosmetic glitch. It aborts the request. Whatever the visitor was doing does not complete, and depending on where the fatal fires, the damage ranges from annoying to expensive.

  • Front-end fatals lose visitors and revenue. A fatal on a product template, a checkout callback, or a form handler turns a working page into a dead end. A WooCommerce store that fatals at the payment step can write the order row but never fire the post-checkout hooks, so the customer is charged and receives no confirmation. Those become support tickets, not just lost sales.
  • Admin-only fatals lock you out of the fix. When the fatal is in a plugin that loads in wp-admin, you cannot reach the dashboard to disable it. Now you are editing files over SFTP under pressure, which is exactly when a second mistake happens.
  • Intermittent fatals are the worst kind. A fatal that only fires on one route, for logged-in users, or under memory pressure will pass every spot-check you run and reappear the moment you stop looking. The site "works when I test it" and breaks for real traffic.
  • Symptom-first fixes waste the outage. Renaming the plugins folder makes the white screen disappear, but you have not learned which plugin, which method, or which trigger. The fatal is still latent. It returns on the next clone, restore, or matching auto-update.

3. Why It’s Hard to Spot

WordPress does almost nothing to help you here. When PHP hits a fatal, the request dies before WordPress can render a useful error, and by default the message never reaches the browser at all. What you see instead depends on settings you probably did not configure deliberately.

If WP_DEBUG_DISPLAY is off (the production default), the visitor gets a generic critical-error page and the detail lands in wp-content/debug.log, but only if WP_DEBUG_LOG is on. If it is not, the detail goes to the PHP-FPM or Apache error log, whose path you may not know and may not be able to read on managed hosting. On some hosts the fatal is suppressed entirely and there is no record anywhere the site owner can reach.

Even when you find the fatal line, it names the place the runtime gave up, not the reason. Call to undefined method on line 248 of some plugin is not a bug on line 248. It is a contract that line 248 relied on, removed somewhere else, minutes or hours earlier. The log entry that would explain it, a warning naming the vanishing symbol or a state change recording the update that removed it, sits in a different file with a different timestamp format and no obvious link to the fatal.

Standard tooling does not close that gap. Uptime monitors report the 500 after the fact. Most application monitors show the fatal as an isolated event with no precursor. None of them line the fatal up against the plugin auto-update that fired three minutes before it. That correlation is the whole answer, and nothing in the default stack draws it for you.

4. Cause

The wp_php_fatals_total signal fires when PHP stops a request abnormally: an uncaught error or exception, a call to an undefined function or method, a type error under PHP 8, or memory exhaustion. WordPress cannot catch these the way it catches its own exceptions, so the request ends where the fatal was raised.

Most WordPress fatals fall into a small number of causes, and each leaves a distinct trace in the signals around it:

  • A plugin or theme update changed a contract. An auto-update replaced a class file and removed or renamed a method another plugin still calls. The update lands as wp_state_changes_total, the first affected request emits wp_php_warnings_total naming the symbol, then a later request promotes it to wp_php_fatals_total.
  • The PHP version moved under the code. A host upgrade to PHP 8.x turns tolerated 7.x patterns into fatal type errors. This shows up as wp_environment_changes_total recording the runtime change, usually preceded by a rising wp_php_warnings_total count from deprecations. The deprecation guide covers this precursor in detail.
  • The request ran out of memory. A fatal that reads Allowed memory size ... exhausted is not a code contract at all. It correlates with wp_request_peak_memory_mb climbing and wp_memory_near_limit_total firing on the route before the crash. The memory-limit guide is the right path for these.
  • A conflict surfaced only under a specific condition. Two plugins coexist until a shared hook, a particular user role, or a single URL brings their incompatible code into the same request. The fatal is real but rare, so it appears as a low, steady wp_php_fatals_total on one route while the rest of the site looks healthy.

Logystera emits the fatal, the warnings before it, and the state or environment change before those as separate signals on one timeline. That is what lets you read the cause instead of guessing at it.

5. Solution

5.1 Diagnose (logs first)

Do not start by disabling plugins. Start by reading the fatal. It tells you the file, the line, and the symbol, which is most of the answer.

1. Find the fatal line. Check the WordPress debug log first, then the server error log if that is empty.

# WordPress debug log (if WP_DEBUG_LOG is on)
grep -nE "PHP Fatal error" /var/www/wp-content/debug.log | tail -n 20

# If that is empty, the PHP-FPM or Apache error log has it
grep -nE "PHP Fatal error" /var/log/php-fpm/error.log | tail -n 20
grep -nE "PHP Fatal error" /var/log/apache2/error.log | tail -n 20

Take the first fatal in the cluster, not the newest. Later entries are usually the same fatal firing again. A typical line looks like this:

[14-Jul-2026 14:05:11 UTC] PHP Fatal error: Uncaught Error: Call to undefined method WC_Order::get_meta_data() in /wp-content/plugins/extra-checkout/handler.php:248

Read it literally. The file and line (handler.php:248) tell you where PHP gave up. The symbol (WC_Order::get_meta_data()) tells you what it could not find. The plugin path tells you which code was running. This produces the wp_php_fatals_total signal.

2. Turn on logging if there is nothing to read. If both logs are empty, the fatal is being suppressed. Enable it in wp-config.php, reproduce the crash once, then read the log and turn display back off.

// In wp-config.php, above the "stop editing" line
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );     // writes to wp-content/debug.log
define( 'WP_DEBUG_DISPLAY', false ); // keep errors out of the browser

3. Pin the time and look 5 minutes earlier. Note the fatal's timestamp. Then look at what changed just before it. A plugin auto-update, a theme switch, or a core update in the minutes before the first fatal is almost always the trigger.

# Warnings just before the fatal often name the exact vanishing symbol
grep -nE "PHP (Warning|Deprecated)" /var/www/wp-content/debug.log \
  | grep -B2 -A2 "get_meta_data"

# Recent plugin/theme file changes (last 2 hours)
find /var/www/wp-content/plugins /var/www/wp-content/themes \
  -type f -name '*.php' -mmin -120 -printf '%TY-%Tm-%Td %TH:%TM  %p\n' | sort

In Logystera the same three steps are one timeline: wp_php_fatals_total anchors the moment, wp_php_warnings_total in the seconds before names the symbol, and wp_state_changes_total or wp_environment_changes_total before that names the trigger.

5.2 Root Causes

Map the fatal message to its cause before you touch anything. The message text is usually enough to place it.

  • Call to undefined function/method after an update means a removed or renamed symbol. Cause: a plugin or theme changed a contract another plugin depends on. Signal pair: wp_state_changes_total then wp_php_fatals_total.
  • Uncaught TypeError or must be of type ... given means the PHP version is stricter than the code. Cause: a host PHP upgrade. Signal: wp_environment_changes_total with rising wp_php_warnings_total.
  • Allowed memory size ... exhausted is a resource fatal, not a code bug. Cause: an unbounded query or import. Signal: wp_request_peak_memory_mb and wp_memory_near_limit_total.
  • Cannot redeclare or an autoload failure means two copies of the same class, often a plugin bundling a library the core or another plugin already loads. Cause: a plugin conflict that only triggers on the route where both load.

5.3 Fix

Match the fix to the cause. Restoring access comes first, but do not stop there, because a restored site with a latent fatal is still broken.

Regain access. If the fatal is in a plugin and you cannot reach wp-admin, disable that one plugin by renaming its folder over SFTP or with WP-CLI. Disable the single culprit, not everything, so you keep the evidence of which one it was.

# Deactivate just the offending plugin
wp plugin deactivate extra-checkout

# Or rename its folder if WP-CLI is unavailable
mv wp-content/plugins/extra-checkout wp-content/plugins/extra-checkout.off

Fix the contract break. If an update removed a symbol another plugin calls, the correct fix is to update the calling plugin to a version that matches, or to roll the changed plugin back to the version that still provided the symbol. Patching the crash line directly only moves the fatal to the next caller.

Fix the PHP mismatch. For a type-error fatal after a PHP upgrade, update the plugin or theme to a PHP-8-compatible release. If none exists, pin the site to the previous PHP version at the host level while you find a maintained replacement. Clear the deprecations first so the next upgrade does not repeat this.

Fix the memory fatal. Raising WP_MEMORY_LIMIT is a stopgap, not a fix. Find the route or query driving wp_request_peak_memory_mb up and bound it. See the memory-limit guide.

Fix the conflict. For a redeclare or autoload fatal, identify the two plugins loading the same class and remove or replace one. If both are required, contact the vendor whose plugin bundles the shared library without a namespace, because that is the actual defect.

5.4 Verify

A fatal is fixed when the signal stops, not when the page loads once. Reproduce the exact request that crashed and watch for a clean run.

# Confirm no new fatals since your fix timestamp
grep -nE "PHP Fatal error" /var/www/wp-content/debug.log \
  | awk -F']' '$1 > "[14-Jul-2026 14:30:00 UTC"'

# Re-run the request that failed and check the status
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/checkout/

In the metric, wp_php_fatals_total should stop incrementing on the affected route. Because the intermittent case is the dangerous one, watch it for a full traffic cycle, not just a manual reload. A fatal that only fired for logged-in users or on one hook will pass an anonymous spot-check and still be live. Healthy state is a flat wp_php_fatals_total across all routes and no fresh wp_php_warnings_total naming the symbol you just fixed.

6. How to Catch This Early

Fixing a fatal is straightforward once you can read it. The hard part is knowing it happened at all, and knowing before your visitors tell you.

This issue surfaces as wp_php_fatals_total.

The reason fatals feel like ambushes is that the site does not tell you when one fires. A fatal on a low-traffic route, on a form handler, or for logged-in users only can run for days before anyone reports it. The homepage looks fine, uptime is green, and the checkout has been quietly failing for a segment of visitors the whole time.

A count of fatals over time answers the question uptime checks cannot: is the site raising errors right now, on which route, and did the rate step up right after a change. The first fatal after a plugin auto-update at 02:14 shows up at 02:14, on the route it broke, next to the wp_state_changes_total that triggered it. That is minutes after the break, not the morning after when the tickets arrive.

Nothing in default WordPress alerts on this. There is no dashboard notice, no email, no built-in record that survives the request. The fatal exists in a log file for as long as that log is retained, which on shared hosting is often not long. Reading it from the logs and counting it as a signal is what turns a fatal from something you discover into something you are told about.

7. Related Silent Failures

  • wp_php_warnings_total spike: a flood of warnings or deprecations naming a symbol, usually the last audible signal before that symbol becomes a fatal. Covered in the PHP warning spike guide.
  • wp_state_changes_total after an auto-update: a plugin or theme changed under the site, the most common trigger for a fatal that "came from nowhere."
  • wp_request_peak_memory_mb climbing: a route creeping toward the memory ceiling, the precursor to an Allowed memory size exhausted fatal.
  • wp_environment_changes_total: a PHP or core version moved under the code, turning tolerated patterns into fatal type errors. See environment drift.
  • wp_rest_errors_total surge: a fatal inside a REST handler shows up as failing /wp-json requests rather than a white screen, easy to miss because the front end still renders. See the 500 error guide.

See what's actually happening in your WordPress system

Connect your site. Logystera starts monitoring within minutes.

Copyright © 2026 Logystera. Operated by 1969730 Ontario Inc., an Ontario, Canada corporation. All rights reserved.