What You're Seeing
PHP notices show up as raw text dumped directly into your page output โ visible on the frontend, in the admin panel, or buried in your HTML source when you hit View Source. They look like this:
Notice: Undefined index: sidebar_position in /var/www/html/wp-content/themes/mytheme/functions.php on line 42
Notice: Undefined variable: post_id in /var/www/html/wp-content/plugins/myplugin/class-plugin.php on line 118
Notice: Trying to get property 'ID' of non-object in /var/www/html/wp-content/themes/mytheme/single.php on line 27
On their own, they won't take your site down. But they clutter your HTML output, corrupt JSON from AJAX calls, and break REST API consumers that choke on unexpected text before a JSON body. Worse, they're usually a symptom of code skipping basic safety checks โ and that's exactly where real bugs hide.
Why This Happens
PHP throws a notice whenever code reads an array key or variable that hasn't been defined yet. That's the root cause โ but WordPress makes it visible.
WP_DEBUG in wp-config.php enables error display. Once it's on, any theme, plugin, or custom code that touches an undefined value will surface as a notice. The usual suspects:
- Accessing
$_GET,$_POST, or$_SERVERkeys without checking they exist - Calling
get_post_meta()and using the result without a null check - Theme options or Customizer settings that haven't been saved yet
- Plugin code that runs on hooks before the data it needs is available
Step 1 โ Locate Where Notices Come From
You need the full file path and line number before you can fix anything. Enable logging but hide output from the frontend:
// wp-config.php
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true); // logs to /wp-content/debug.log
define('WP_DEBUG_DISPLAY', false); // hides from frontend, logs instead
Setting WP_DEBUG_DISPLAY to false immediately kills notices on the frontend while still writing them to the log. Now tail it:
tail -f /var/www/html/wp-content/debug.log
Reload the page that showed the notice. The log will show the exact file and line.
Step 2 โ Fix the Undefined Index/Variable
The file and line number point you straight to the problem. The fix almost always matches one of these four patterns:
Pattern A: Undefined array index
// BROKEN โ triggers Notice
$value = $options['sidebar_position'];
// FIXED โ use isset() or null coalescing
$value = isset($options['sidebar_position']) ? $options['sidebar_position'] : 'right';
// PHP 7+ shorthand:
$value = $options['sidebar_position'] ?? 'right';
Pattern B: Undefined variable
// BROKEN
echo $post_id;
// FIXED
$post_id = $post_id ?? null;
if ($post_id) {
echo $post_id;
}
Pattern C: Undefined $_GET / $_POST / $_REQUEST key
// BROKEN โ common in plugin form handlers
$action = $_POST['my_action'];
// FIXED
$action = sanitize_text_field($_POST['my_action'] ?? '');
if (empty($action)) {
return;
}
Pattern D: get_post_meta() returning empty string
// BROKEN โ $meta is empty string when meta doesn't exist
$meta = get_post_meta($post->ID, 'custom_field', true);
echo $meta['key']; // Notice if $meta is '' not an array
// FIXED
$meta = get_post_meta($post->ID, 'custom_field', true);
if (is_array($meta) && isset($meta['key'])) {
echo $meta['key'];
}
Step 3 โ Handle Third-Party Plugin or Theme Notices
When the notice comes from a plugin or theme you didn't write, you have two options.
Option A: Suppress at the PHP level (quick fix)
Add this to wp-config.php to keep logging real errors while silencing notices and deprecations:
// wp-config.php โ suppress notices from third-party code
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT);
Option B: Report to the plugin/theme author
Copy the full notice โ file path, line number, and all โ and open an issue in the plugin's repository. Notices in actively maintained plugins are treated as bugs and usually get patched within a release or two.
Step 4 โ If Notices Are Breaking JSON/AJAX Responses
Notices bleeding into JSON responses aren't just cosmetic โ they actively break things. Any text prepended to a JSON body causes JSON.parse to throw in the browser:
// What your AJAX handler actually sends:
Notice: Undefined index: nonce in /wp-content/plugins/myplugin/ajax.php on line 5
{"success":true,"data":"result"}
// Browser sees malformed JSON โ your JS breaks
Start with WP_DEBUG_DISPLAY false (Step 1) and fix the undefined index in your AJAX handler. If stray output is still slipping through, wrap the handler in ob_start() and ob_end_clean() to capture and discard it before wp_send_json() fires:
add_action('wp_ajax_my_action', function() {
ob_start(); // capture any notice output
$nonce = $_POST['nonce'] ?? '';
if (!wp_verify_nonce($nonce, 'my_action')) {
ob_end_clean();
wp_send_json_error('Invalid nonce');
}
ob_end_clean(); // discard anything that leaked out
wp_send_json_success(['result' => 'ok']);
});
Verify the Fix
-
Reload the page that was showing notices. The notice text should be gone from the HTML.
-
Confirm the log is clean:
tail -20 /var/www/html/wp-content/debug.log
-
Fixed an AJAX endpoint? Open DevTools โ Network, trigger the action, and confirm the response is valid JSON with no text prepended.
-
Run a quick syntax check on any file you edited:
php -l wp-content/themes/mytheme/functions.php
Production Checklist
- Set
WP_DEBUGtofalseon production โ notices on live sites expose internal file paths to visitors - Keep
WP_DEBUG_LOGset totrueon production so errors are captured silently - Rotate
wp-content/debug.logโ a busy WooCommerce store can generate 200MB+ in a single day without log rotation - Use a staging environment to test with
WP_DEBUG_DISPLAY truebefore pushing to production

