Call to Undefined Function: Which File Was Supposed to Define It
The function exists somewhere — in wp-admin, in a plugin that is off, in pluggable.php that has not loaded yet, or in a PHP extension the server lacks. The function name tells you which, and the admin-only case fails only for visitors.
PHP was asked to run a function that no loaded file has defined; the function usually exists, just in a file that was not loaded for this request. The full line reads PHP Fatal error: Uncaught Error: Call to undefined function download_url() in /var/www/html/wp-content/themes/mytheme/functions.php:88, followed by a stack trace. Visitors see "There has been a critical error on this website."
Read the function name before anything else. Where it normally lives tells you why it is missing.
Classify the function name
| Function | Defined in | Why it is missing |
|---|---|---|
download_url(), wp_handle_upload(), wp_generate_attachment_metadata(), media_handle_upload() |
wp-admin/includes/… |
Called outside the admin, where those files are not loaded |
wp_get_current_user(), is_user_logged_in(), wp_mail(), wp_create_nonce() |
wp-includes/pluggable.php |
Called in a plugin file body, before that file loads |
wc_get_product(), a custom-fields helper, any prefix_…() |
Another plugin | That plugin is inactive, deleted, or failed to load |
create_function(), each(), money_format() |
Old PHP | Removed in PHP 8.0 |
mysqli_connect(), curl_init(), imagecreatefromwebp(), mb_strtolower() |
A PHP extension | Extension not installed or not enabled |
Get the file and line from debug.log with debug logging on. If wp-admin is down, the critical error guide covers recovery mode and deactivating the culprit.
Admin-only functions called on the front end
WordPress loads most of wp-admin/includes/*.php on admin screens only. A theme or plugin whose front-end form calls media_handle_upload(), or whose cron job calls download_url(), works perfectly while the developer tests in the dashboard, then fatals for the first visitor, REST request or cron run.
One exception changed recently: since WordPress 6.8, core loads wp-admin/includes/plugin.php on every request, so is_plugin_active() and get_plugins() only fail this way on older versions. The other admin files still need loading explicitly wherever you call their functions:
if ( ! function_exists( 'download_url' ) ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
}
$tmp = download_url( $url );
if ( is_wp_error( $tmp ) ) {
// …
}
For a dependency check, class_exists( 'WooCommerce' ) or function_exists( 'wc_get_product' ) is more reliable than is_plugin_active(), which knows nothing about whether the plugin actually loaded. The media functions need wp-admin/includes/image.php, file.php and media.php together.
Pluggable functions called too early
wp-includes/pluggable.php defines functions that plugins are allowed to replace, so WordPress includes it after all active plugins. Calling wp_get_current_user() or is_user_logged_in() directly in a plugin's main file, outside any hook, runs before the file exists:
// Fails: runs when the plugin file is included.
if ( is_user_logged_in() ) { … }
// Works: runs once WordPress has set up the user.
add_action( 'init', function () {
if ( is_user_logged_in() ) { … }
} );
Moving the call into a hook also gives correct results: the current user is only determined just before init.
A plugin the code depends on is off
Theme templates are the classic case: a template calls a WooCommerce or custom-fields function, someone deactivates that plugin, and every page using the template fatals. The fix depends on intent:
- If the plugin is required, reactivate it. A dependent plugin can declare the
Requires Pluginsheader, which removes the dependency's Deactivate link on the Plugins screen while a dependent is active; a theme has no such header and needs an admin notice instead. - If the feature is optional, guard the call:
if ( function_exists( 'wc_get_product' ) ).
A plugin that failed to load because of its own fatal error also leaves its functions undefined; the first error in the log is the one to fix. Conflict testing tells you which plugin is involved if the stack trace is unclear.
PHP removed the function, or never had it
After a PHP upgrade, old code calling create_function() or each() stops working — both were removed in PHP 8.0. Update the plugin or theme; if it is abandoned, replace it. Similar breakage often arrives in a batch, covered in site not working after a PHP update.
Extension functions fail when the extension is missing from the PHP that serves the site: mysqli_* without mysqli (its own article), curl_* without curl, mb_* without mbstring. WordPress polyfills a few of these itself (mb_substr(), mb_strlen() and a couple of others), which is why a site can mostly work and fail only in a plugin that calls mb_strtolower(). Check Tools › Site Health › Info › Server for the web server's PHP, not php -m on the command line.
Typos and namespaces
If the function is your own: a misspelt name, or a function declared inside a namespace and called without it. Inside namespace Acme;, function helper() is really Acme\helper(). Calling helper() from a file with no namespace fails with Call to undefined function helper(). Use the full name or a use function statement.
Common mistakes
- Copying a function definition from
wp-admin/includesintofunctions.php, which later fatals with Cannot redeclare when the admin loads the real one. - Testing a fix only in wp-admin, where the admin-only functions exist anyway.
- Wrapping a required dependency in
function_exists()so the page renders without the feature and nobody notices. - Fixing the second error in the log instead of the first.
Verify
- Load the failing URL logged out, in a private window; admin-only bugs only show there.
- Trigger cron with
wp cron event run --due-nowand checkdebug.logfor new "undefined function" lines. - Request a REST endpoint of the site (
/wp-json/) and confirm it returns JSON, not a critical error page.
Frequently asked
- It is defined in wp-admin/includes/file.php, which WordPress loads on admin screens but not for normal front-end, REST or cron requests. Code that calls it outside the admin has to load that file first with require_once ABSPATH . 'wp-admin/includes/file.php'. is_plugin_active() used to fail the same way, until WordPress 6.8 started loading wp-admin/includes/plugin.php on every request.
- PHP 8.0 removed create_function(), each() and money_format(), among others. Code that still calls them fails with Call to undefined function on PHP 8. The fix is to update the plugin or theme; there is no setting that brings them back.
- It is the class version: an object or class does not have the method being called. The usual WordPress causes are an outdated plugin calling a method a newer version of another plugin removed, or the reverse. The fix is the same idea: match versions, or guard the call with method_exists().