Fatal error: Call to undefined function get_option() almost never means WordPress is broken. It means PHP reached a call to get_option() (or wp_insert_post(), or current_user_can(), or any other WordPress function) before wp-load.php had a chance to define it. The fix is to make sure WordPress is loaded before your code runs, or to restructure the code so it runs from inside a WordPress request lifecycle.
How do I fix 'Call to undefined function get_option()' in WordPress?
The error is raised when PHP encounters get_option() but the WordPress runtime isn't loaded — get_option only exists after wp-includes/option.php has been included by wp-settings.php, which is included by wp-load.php, which is the entry point for every WordPress request. If a standalone PHP file or a cron script is calling WordPress functions without bootstrapping WordPress first, the function doesn't exist yet and PHP throws a fatal.
The fast fix for standalone scripts that genuinely need WordPress functions: bootstrap WordPress by requiring wp-load.php before your code runs. The better fix for plugin and theme code: never call WordPress functions at file-load time. Wrap everything in a callback hooked to init, plugins_loaded, or whichever WordPress hook fires after the runtime is ready.
Jump to:
- Root cause: when get_option() doesn't exist yet
- The most common scenarios
- The fast fix: bootstrap with wp-load.php
- The modern preferred approach: WP-CLI
- Hook into init instead of file-level scope
- Plugin developer checklist
- PHP 8.x vs PHP 7.x error format
- Troubleshooting matrix
- What to do next
- FAQ
Root cause: when get_option() doesn't exist yet
The WordPress function load order is, simplified:
HTTP request → index.php → wp-blog-header.php → wp-load.php → wp-config.php → wp-settings.php
↓
wp-includes/option.php (defines get_option)
wp-includes/plugin.php (defines do_action)
...500+ more files...
↓
plugins_loaded action
↓
init action
↓
the request proceeds
Any code that calls get_option() before that chain has completed gets the fatal error. The original 2015 framing of this error as "corrupted core files" is misleading in the vast majority of cases — corrupted core is one possible cause, but a far rarer one than premature function calls.
The most common scenarios
The five places I actually see this error in 2026:
1. A standalone PHP file accessed directly
A developer drops a file like /wp-content/themes/mytheme/scripts/import.php and hits it from the browser at https://example.com/wp-content/themes/mytheme/scripts/import.php. PHP serves the file directly — wp-load.php is never executed for that URL. The first get_option() call inside the file throws.
2. Custom cron script run from system cron
# /etc/cron.d/site-cron
0 * * * * www-data php /var/www/html/wp-content/scripts/cleanup.phpSame problem: cleanup.php was invoked directly. PHP didn't go through Apache or Nginx, so the WordPress request lifecycle never fired.
3. Plugin code at file-level scope
// Bad: runs at require() time, before init has fired
$option = get_option( 'my_plugin_settings' );If this is at the top of your plugin file and the plugin is loaded by wp-settings.php before all required files are included, get_option may not be defined yet — or worse, it's defined but the database connection isn't ready and get_option() returns garbage.
4. Composer autoload conflicts
A plugin uses Composer's autoloader and includes a class that calls get_option() in its constructor. If the class is instantiated during composer.json post-install hooks (or anywhere outside a normal WordPress request), get_option isn't loaded.
5. Truly corrupted core files
Rare in 2026 — but if you can confirm wp-includes/option.php is missing or contains a parse error, then yes, a core file replacement is the fix. This was much more common in shared-hosting eras when files genuinely got corrupted during FTP uploads.
The fast fix: bootstrap with wp-load.php
For a standalone script that legitimately needs WordPress functions, require wp-load.php at the top:
<?php
// /var/www/html/wp-content/scripts/cleanup.php
require_once dirname( __FILE__ ) . '/../../../wp-load.php';
// Now WordPress is loaded
$option = get_option( 'siteurl' );
echo $option;The path navigation dirname( __FILE__ ) . '/../../../wp-load.php' walks three directories up from wp-content/scripts/ to the WordPress root. Adjust the count based on where your script actually lives.
This works but has costs:
- It loads the entire WordPress stack — every plugin, every theme function, all the database connections. For a five-line cleanup task, that's a lot of overhead.
- Errors in any active plugin during bootstrap will kill your script.
- You inherit the request's user context (or none, for CLI), which can lead to permission-related surprises.
For one-off cleanup scripts it's fine. For anything you'll run regularly, prefer WP-CLI.
The modern preferred approach: WP-CLI
WP-CLI handles bootstrapping for you and gives you a per-command API:
# Read a single option
wp option get siteurl
# List all options matching a pattern
wp option list --search='my_plugin_*' --format=table
# Update an option
wp option update my_plugin_settings '{"key":"value"}' --format=json
# Run arbitrary PHP with WordPress loaded
wp eval 'echo get_option("siteurl");'
# Run a PHP file with WordPress loaded
wp eval-file path/to/script.phpwp eval-file is the modern replacement for the "require wp-load.php" pattern. WP-CLI loads WordPress once and runs your file in that context. From cron:
0 * * * * www-data /usr/local/bin/wp --path=/var/www/html eval-file /var/www/html/wp-content/scripts/cleanup.php --quietThe --path flag tells WP-CLI where the WordPress install lives. --quiet suppresses non-error output (cron will email anything that hits stdout otherwise).
For one-off interactive debugging, wp shell drops you into a REPL with WordPress loaded — far better than dropping var_dump() calls into a theme file.
Hook into init instead of file-level scope
Inside a plugin or theme, never call WordPress functions at the top level of the file. The right pattern:
<?php
/**
* Plugin Name: My Settings Reader
*/
// BAD: runs before WordPress may be ready
// $option = get_option( 'my_plugin_settings' );
// GOOD: defer until init
function my_plugin_load_settings() {
$option = get_option( 'my_plugin_settings' );
// ... use $option
}
add_action( 'init', 'my_plugin_load_settings' );init fires after WordPress core, all active plugins, and the active theme's functions.php have finished loading. At that point every WordPress function is available, the database is connected, the current user is resolved, and you're safe to call anything.
For code that needs WordPress functions but must run before init (rare), use plugins_loaded — fires once all plugins are loaded but before init:
function my_plugin_early_setup() {
// get_option is available here
if ( get_option( 'my_plugin_feature_flag' ) ) {
// register early hooks
}
}
add_action( 'plugins_loaded', 'my_plugin_early_setup' );Plugin developer checklist
When writing a plugin, the rules that prevent this error:
- No WordPress function calls outside a function body. Variable assignments at file scope using
get_option()are the most common offender. - No database queries before
plugins_loaded.$wpdbis available earlier but using it before plugins are loaded can race with other plugins' table-creation hooks. - Use
register_activation_hook()for one-time setup. Don't run install logic at file load. - Guard against direct file access. Add
if ( ! defined( 'ABSPATH' ) ) { exit; }at the top of every PHP file in your plugin. - Don't include files that include WordPress. A plugin file that does
require 'wp-load.php'creates an infinite-loop scenario when WordPress loads the plugin.
The ABSPATH guard is the canonical defense against the entire class of "scripts being accessed directly" bugs:
<?php
/**
* Plugin Name: My Plugin
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Prevent direct file access
}
// ... rest of pluginIf a curious visitor hits https://example.com/wp-content/plugins/my-plugin/my-plugin.php directly, PHP would otherwise execute the file with no WordPress context. The ABSPATH check turns that into a clean exit.
PHP 8.x vs PHP 7.x error format
The error message text shifted slightly between PHP versions:
| PHP version | Error text |
|---|---|
| PHP 7.x | Fatal error: Call to undefined function get_option() in /path/to/file.php on line 12 |
| PHP 8.0+ | Fatal error: Uncaught Error: Call to undefined function get_option() in /path/to/file.php:12 |
PHP 8 wraps the fatal in an Error exception that includes a stack trace, which is significantly more useful for debugging. If you're still on PHP 7.x and seeing the older format, enabling display_errors and error_reporting=E_ALL in your wp-config.php will at least show the file and line number:
// wp-config.php (development only)
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
@ini_set( 'display_errors', 0 );Errors go to wp-content/debug.log rather than the browser, which is safer in production-adjacent environments.
If the error is hitting your script before WordPress is loaded enough to set those constants, fall back to native PHP error logging:
ini_set( 'log_errors', '1' );
ini_set( 'error_log', '/var/log/php-errors.log' );Troubleshooting matrix
| Symptom | Likely cause | Fix |
|---|---|---|
| Error in a standalone script | Script doesn't bootstrap WordPress | Add require_once '/path/to/wp-load.php'; or run via WP-CLI |
| Error in plugin activation | get_option called at file scope | Wrap in a function and hook to register_activation_hook() |
| Error in cron job | Script invoked directly by cron | Switch cron to call wp eval-file via WP-CLI |
| Error only on multisite | ms_files_handler.php or premature multisite call | Use is_multisite() only after plugins_loaded |
| Error after composer install | Class instantiated outside WordPress lifecycle | Move construction to a hook callback |
| Error after PHP upgrade | Plugin used a function deprecated in newer PHP | Check error_log for "deprecated" notices; update plugin |
| Error after WordPress core update | Genuinely corrupted core files | Re-download matching WordPress version, overwrite wp-admin and wp-includes |
| Error only at admin URL | Plugin loads before pluggable.php in admin | Hook to admin_init instead of init |
| ABSPATH not defined | Direct file access bypassing index.php | Add if ( ! defined( 'ABSPATH' ) ) { exit; } and re-route via index.php |
For specific WordPress-related PHP issues:
- If memory exhausts before
get_optionis reached, see how to increase the PHP memory limit. TheWP_MAX_MEMORY_LIMITconstant is the relevant lever. - If a bulk import script is hitting both
get_optionerrors and memory pressure, see wp_insert_post consuming large amounts of memory. - If the error blocks admin access, see how to change a WordPress password for direct database recovery once you've fixed the underlying issue.
What to do next
For related WordPress troubleshooting:
- PHP memory limit reference — adjacent fatal-error pattern (
Allowed memory size exhausted) with the same "fix the script, then maybe raise the limit" framing. - wp_insert_post memory deep-dive — common follow-on issue when scripts that triggered
get_optionerrors are bulk-importing content. - Change a WordPress password — admin recovery via the database when a plugin error has locked you out of wp-admin.
- WordPress: moderate comments using regular expressions — adjacent reference if your plugin error is in a comment-moderation hook.
- How to use ElasticPress with WP_Query — search-layer reference for sites that hit this error during indexing.
- WPScan usage and man page — security scan to confirm a fatal error wasn't caused by a compromised plugin.





