This is one of the most embarrassing and most easily made deploy mistakes there is. I was tidying up the functions.php of a high-traffic WordPress site I maintain, splitting code into cleaner inc/*.php files. I added one line:
require_once get_template_directory() . '/inc/social-share.php';I uploaded functions.php through WP File Manager (the plugin I deploy with). Then the site went completely down. wp-admin showed:
There has been a critical error on this website.
And here's the part that made me break out in a cold sweat: WP File Manager itself lives inside wp-admin. Because the fatal error fired on every request — admin included — I had lost the very tool I was going to use to fix it. The recovery channel went down with the site.
Root cause: functions.php loads on EVERY request
The cause was trivial: I uploaded a functions.php that require_once'd inc/social-share.php — but I hadn't uploaded the new file yet. The require_once target didn't exist, so PHP threw a fatal error.
functions.php isn't an ordinary file. WordPress loads it on every request, including the admin dashboard. So a require_once pointing at a missing file will fatal the whole site, including the tool you were going to use to fix it. This isn't an error confined to the frontend; it touches everything.
Prevention #1: deploy order matters
The rule is simple but easily broken in a hurry: upload dependencies before their callers.
- Upload the new
inc/*.phpfiles first. - Then upload the
functions.phpthat requires them.
If you can deploy atomically (rsync, git deploy, a zip extracted in one go), do that so there's no window in which the new functions.php exists but its dependency doesn't.
Prevention #2: wrap includes defensively
When you deploy via a file manager — where uploads aren't atomic — never trust that a dependency file is already there. Wrap every include in file_exists():
if ( file_exists( $f = get_template_directory() . '/inc/social-share.php' ) ) {
require_once $f;
}With this, a missing file degrades gracefully — the social-share feature simply doesn't appear, but the site stays up and wp-admin stays reachable. Far better than a white "critical error" screen.
Recovery once you're already locked out
If the site is already down and wp-admin is unreachable, forget every plugin — you can't open a single one. You need file access from outside WordPress:
- Log in via SFTP or cPanel File Manager (these live outside WordPress, so they stay up).
- Do one of:
- Upload the missing file (
inc/social-share.php) — the fatal clears immediately; or - Comment out / delete the
require_onceline fromfunctions.phpso it stops looking for the missing file.
- Upload the missing file (
- Reload the site. Once the fatal is gone, wp-admin (and WP File Manager) come back.
A milder variant of the same class of bug
This bug has a cousin with a different symptom but an identical cause — reversed deploy order. I added a new function to an existing inc file, then uploaded the caller first, before the updated inc file:
Fatal error: Call to undefined function my_new_social_share()
The function doesn't exist yet because the file that defines it hasn't been uploaded. Same pattern exactly: caller precedes definition → fatal.
Deploy checklist for anything loaded on every request
- Upload dependencies before callers — new
inc/*.phpfiles first,functions.phplast. - Where possible, deploy atomically (git/rsync/zip) to eliminate the dangerous window.
- Wrap includes in
file_exists()when deploying via a file manager — a missing file should degrade, not fatal. - Don't rely on a tool that lives inside wp-admin as your only recovery channel — keep SFTP/cPanel access ready.
- When locked out: upload the missing file or remove the require line over SFTP — not through any plugin.
The lesson: for anything loaded on every request, deploy order matters. Upload dependencies before their callers, and guard your includes with file_exists() every time you deploy through a tool that lives inside the very site you're changing.
