AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Wp Debugging

skill-iwritec0de-wp-dev-wp-debugging · by iwritec0de

>-

No reviews yet
0 installs
23 views
0.0% view→install

Install

$ agentstack add skill-iwritec0de-wp-dev-wp-debugging

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-iwritec0de-wp-dev-wp-debugging)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
5mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Wp Debugging? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

WordPress Debugging Methodology

Systematic debugging for WordPress and PHP. Four phases: root cause investigation, pattern analysis, hypothesis testing, implementation. No guessing. No shotgun fixes.


Critical Rules

  • Never apply a fix without identifying the root cause. Guessing wastes time and introduces new bugs.
  • Enable WP_DEBUG first. Every debugging session starts by enabling debug mode. No exceptions.
  • Check error logs before guessing. The answer is almost always in debug.log, the PHP error log, or the server error log. Read them.
  • One change at a time. If you change two things and the bug disappears, you do not know which change fixed it. Revert one and verify.
  • Always test in staging. Never debug production by pushing untested changes to it. Clone the environment or use wp-env.
  • Document what you find. Leave a comment explaining the root cause at the fix site. Future developers (including you) will thank you.

Phase 1: Root Cause Investigation

Before forming any hypothesis, gather evidence. This phase is about observation, not action.

1.1 Enable WordPress Debug Mode

Add these constants to wp-config.php above the /* That's all, stop editing! */ line:

// Enable debug mode — shows PHP errors and WordPress deprecation notices.
define( 'WP_DEBUG', true );

// Log errors to wp-content/debug.log instead of displaying on screen.
define( 'WP_DEBUG_LOG', true );

// Do NOT display errors on the frontend (security risk on production).
define( 'WP_DEBUG_DISPLAY', false );
@ini_set( 'display_errors', 0 );

// Load unminified versions of core CSS and JS files (useful for debugging core/Gutenberg).
define( 'SCRIPT_DEBUG', true );

Verify debug mode is active:

wp eval 'echo WP_DEBUG ? "WP_DEBUG is ON" : "WP_DEBUG is OFF";'

1.2 Read the Debug Log

# View the last 50 lines of the WordPress debug log.
tail -50 wp-content/debug.log

# Follow the log in real time while reproducing the bug.
tail -f wp-content/debug.log

# Search for fatal errors.
grep -i "fatal error" wp-content/debug.log | tail -20

# Search for a specific plugin's errors.
grep "my-plugin" wp-content/debug.log | tail -20

# Clear the log before a fresh reproduction attempt.
> wp-content/debug.log

1.3 Read PHP Error Logs

The PHP error log location varies by server configuration:

# Find the configured error log path.
php -r 'echo ini_get("error_log") . PHP_EOL;'

# Common locations.
tail -50 /var/log/php_errors.log
tail -50 /var/log/php-fpm/error.log
tail -50 /var/log/apache2/error.log
tail -50 /var/log/nginx/error.log

# On macOS with MAMP/Homebrew.
tail -50 /usr/local/var/log/php-fpm.log

1.4 Targeted Debugging with error_log()

Use error_log() to trace execution flow and inspect values. This is your printf-debugging for WordPress.

// Log a simple message.
error_log( 'DEBUG: my_function() was called' );

// Log a variable's value.
error_log( 'DEBUG: $post_id = ' . print_r( $post_id, true ) );

// Log an array or object.
error_log( 'DEBUG: $args = ' . print_r( $args, true ) );

// Log a stack trace to see who called this function.
error_log( 'DEBUG: backtrace — ' . wp_debug_backtrace_summary() );

// Conditional logging — only log for a specific post type.
if ( 'product' === get_post_type( $post_id ) ) {
    error_log( 'DEBUG: Processing product #' . $post_id );
}

1.5 WP-CLI Debugging Commands

WP-CLI lets you interrogate WordPress state without touching the browser.

# Evaluate arbitrary PHP in the WordPress context.
wp eval 'var_dump( get_option("active_plugins") );'

# Open an interactive PHP shell with WordPress loaded.
wp shell

# Query the database directly.
wp db query "SELECT option_name, option_value FROM wp_options WHERE option_name = 'active_plugins';"

# Read a specific option.
wp option get siteurl
wp option get active_plugins --format=json

# Check if a plugin is active.
wp plugin is-active woocommerce && echo "Active" || echo "Inactive"

# Get plugin status and version info.
wp plugin list --status=active --format=table

# Check PHP and WordPress versions.
wp --info
wp core version

# Test if a function exists.
wp eval 'var_dump( function_exists("my_custom_function") );'

# Test if a class exists.
wp eval 'var_dump( class_exists("My_Custom_Class") );'

# Check a specific hook's registered callbacks.
wp eval '
global $wp_filter;
if ( isset( $wp_filter["init"] ) ) {
    foreach ( $wp_filter["init"]->callbacks as $priority => $hooks ) {
        foreach ( $hooks as $hook ) {
            $callback = $hook["function"];
            if ( is_array( $callback ) ) {
                $callback = ( is_object( $callback[0] ) ? get_class( $callback[0] ) : $callback[0] ) . "::" . $callback[1];
            }
            error_log( "init @ priority {$priority}: {$callback}" );
        }
    }
}
'

1.6 Query Monitor Plugin

Install Query Monitor for in-browser debugging of hooks, database queries, HTTP requests, and more.

wp plugin install query-monitor --activate

Key panels to check:

  • PHP Errors — caught errors, warnings, notices, deprecations
  • Queries — slow queries, duplicate queries, queries by caller
  • Hooks & Actions — what fired, in what order, what's attached
  • HTTP API Calls — outbound requests, response codes, timing
  • Transients — what was set, what was fetched
  • Environment — PHP version, extensions, memory limits

1.7 Server Error Logs

# Apache error log.
sudo tail -100 /var/log/apache2/error.log

# Nginx error log.
sudo tail -100 /var/log/nginx/error.log

# PHP-FPM log (pool-specific).
sudo tail -100 /var/log/php8.2-fpm.log

# Systemd journal for PHP-FPM.
sudo journalctl -u php8.2-fpm --since "10 minutes ago" --no-pager

Phase 2: Common WordPress Bug Patterns

Use the evidence from Phase 1 to match against these known patterns.

2.1 White Screen of Death (WSOD)

Symptom: Blank white page. No error message. No HTML output at all.

Root cause: Almost always a PHP fatal error that kills execution before any output is sent.

Diagnosis:

# Step 1: Enable WP_DEBUG and check the log.
tail -20 wp-content/debug.log

# Step 2: If no log output, check the PHP error log.
php -r 'echo ini_get("error_log") . PHP_EOL;'

# Step 3: Check what changed recently.
wp plugin list --recently-active --format=table

# Step 4: Try loading WordPress from CLI (bypasses web server issues).
wp eval 'echo "WordPress loaded successfully.";'

Common causes:

  • Syntax error in a recently edited file (Parse error: syntax error, unexpected...)
  • Calling an undefined function (typo or missing dependency)
  • require / include of a file that does not exist
  • Exhausted memory limit

2.2 500 Internal Server Error

Symptom: Server returns HTTP 500. May be intermittent.

Diagnosis:

# Check .htaccess for corruption.
cat .htaccess

# Regenerate .htaccess via WP-CLI.
wp rewrite flush

# Check PHP memory limit.
wp eval 'echo "Memory limit: " . ini_get("memory_limit") . PHP_EOL;'

# Check file permissions (typical: 755 for dirs, 644 for files).
find . -type d ! -perm 755 | head -20
find . -type f ! -perm 644 | head -20

# Check for .htaccess in subdirectories that may conflict.
find . -name ".htaccess" -not -path "./vendor/*" -not -path "./node_modules/*"

Common causes:

  • Corrupted .htaccess (regenerate with wp rewrite flush)
  • PHP memory exhaustion (increase WP_MEMORY_LIMIT)
  • Incorrect file permissions (especially after deployment)
  • PHP version incompatibility (check phpinfo() output)
  • Broken plugin/theme throwing a fatal in a hooked callback

2.3 Plugin Conflicts

Symptom: Feature works in isolation, breaks when another plugin is active.

Diagnosis — systematic deactivation:

# Step 1: Record current active plugins.
wp plugin list --status=active --format=csv > /tmp/active-plugins.csv

# Step 2: Deactivate ALL plugins.
wp plugin deactivate --all

# Step 3: Activate ONLY your plugin. Test. If the bug is gone, it is a conflict.
wp plugin activate my-plugin

# Step 4: Activate other plugins one at a time. Test after each.
wp plugin activate plugin-a
# Test... works? Continue.
wp plugin activate plugin-b
# Test... broken! plugin-b conflicts with my-plugin.

# Step 5: Restore original state after testing.
wp plugin activate $(cat /tmp/active-plugins.csv | tail -n +2 | cut -d',' -f1 | tr '\n' ' ')

Common conflict sources:

  • Two plugins enqueuing different versions of the same JS library (e.g., jQuery UI, Select2)
  • Two plugins hooking the same filter and returning incompatible data
  • Namespace collisions (two plugins defining the same function or class name)
  • Two plugins modifying the same REST API endpoint

2.4 Hook and Filter Issues

Symptom: Callback never fires, fires at the wrong time, or receives wrong arguments.

// Problem: Callback registered with wrong number of accepted_args.
// add_filter signature: add_filter( $hook, $callback, $priority, $accepted_args )
// The filter passes 3 arguments, but accepted_args defaults to 1.

// WRONG — only receives $value, loses $post_id and $meta_key.
add_filter( 'get_post_metadata', 'my_meta_filter', 10 );
function my_meta_filter( $value, $post_id, $meta_key ) {
    // $post_id and $meta_key are undefined here!
}

// CORRECT — explicitly accept 3 arguments.
add_filter( 'get_post_metadata', 'my_meta_filter', 10, 3 );
function my_meta_filter( $value, $post_id, $meta_key ) {
    if ( 'my_key' === $meta_key ) {
        return 'overridden_value';
    }
    return $value;
}

Debugging hook execution order:

// Check if an action has fired (and how many times).
error_log( 'init fired: ' . did_action( 'init' ) . ' times' );

// Check if we are currently inside a specific action.
if ( doing_action( 'save_post' ) ) {
    error_log( 'We are inside save_post right now.' );
}

// Check if another plugin removed your hook.
wp eval '
global $wp_filter;
$has_hook = has_filter( "the_content", "my_content_filter" );
echo $has_hook !== false ? "Hook exists at priority {$has_hook}" : "Hook is MISSING";
'

Common causes:

  • Wrong priority (your callback runs before the data it depends on is set)
  • Missing $accepted_args parameter (defaults to 1)
  • Another plugin called remove_action() / remove_filter() on your hook
  • Hooking too early (e.g., hooking in the global scope before WordPress loads the hook system)

2.5 REST API Errors

Symptom: API returns 401, 403, 500, or malformed responses.

// Problem: Permission callback missing or incorrect.
// WRONG — no permission_callback means WordPress 5.5+ shows a _doing_it_wrong notice.
register_rest_route( 'myplugin/v1', '/items', array(
    'methods'  => 'GET',
    'callback' => 'my_get_items',
) );

// CORRECT — always specify a permission_callback.
register_rest_route( 'myplugin/v1', '/items', array(
    'methods'             => 'GET',
    'callback'            => 'my_get_items',
    'permission_callback' => function () {
        return current_user_can( 'read' );
    },
) );

// Debugging a REST endpoint.
function my_get_items( WP_REST_Request $request ) {
    $items = get_posts( array(
        'post_type'   => 'my_cpt',
        'numberposts' => $request->get_param( 'per_page' ) ?? 10,
    ) );

    if ( empty( $items ) ) {
        // WRONG — do not return raw arrays or wp_send_json.
        // return array();

        // CORRECT — always use rest_ensure_response().
        return rest_ensure_response( array() );
    }

    return rest_ensure_response( $items );
}

Debugging REST API from the command line:

# Test the endpoint directly.
wp eval '
$request  = new WP_REST_Request( "GET", "/myplugin/v1/items" );
$response = rest_do_request( $request );
$data     = $response->get_data();
echo print_r( $data, true );
'

# Check registered routes.
wp eval '
$server = rest_get_server();
$routes = $server->get_routes();
foreach ( $routes as $route => $handlers ) {
    if ( strpos( $route, "myplugin" ) !== false ) {
        echo $route . PHP_EOL;
    }
}
'

# Test with nonce authentication (for cookie-based auth).
# In browser console:
# fetch('/wp-json/myplugin/v1/items', { headers: { 'X-WP-Nonce': wpApiSettings.nonce } })

2.6 Database Errors

Symptom: Data not saving, incorrect data returned, or $wpdb error messages.

// Inspect the last query and error.
global $wpdb;
$results = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}my_table WHERE id = 5" );
if ( $wpdb->last_error ) {
    error_log( 'DB Error: ' . $wpdb->last_error );
    error_log( 'DB Query: ' . $wpdb->last_query );
}

// WRONG — using table name without prefix. This breaks multisite and custom prefixes.
$wpdb->get_results( "SELECT * FROM my_table" );

// CORRECT — always use $wpdb->prefix.
$wpdb->get_results( "SELECT * FROM {$wpdb->prefix}my_table" );

// WRONG — string interpolation in queries (SQL injection risk).
$wpdb->query( "DELETE FROM {$wpdb->prefix}my_table WHERE id = {$_GET['id']}" );

// CORRECT — use $wpdb->prepare() for ALL user-supplied values.
$wpdb->query(
    $wpdb->prepare(
        "DELETE FROM {$wpdb->prefix}my_table WHERE id = %d",
        absint( $_GET['id'] )
    )
);

Debugging queries with SAVEQUERIES:

// Add to wp-config.php (REMOVE after debugging — performance hit).
define( 'SAVEQUERIES', true );

// Then in your code or a mu-plugin:
add_action( 'shutdown', function () {
    global $wpdb;
    error_log( 'Total queries: ' . count( $wpdb->queries ) );

    // Find slow queries (over 0.05 seconds).
    foreach ( $wpdb->queries as $query ) {
        if ( $query[1] > 0.05 ) {
            error_log( sprintf(
                'Slow query (%.4fs): %s | Caller: %s',
                $query[1],
                $query[0],
                $query[2]
            ) );
        }
    }
} );

2.7 Permalink Issues

Symptom: 404 errors on pages that exist. Custom post types or REST routes returning 404.

# Flush rewrite rules via WP-CLI.
wp rewrite flush

# List current rewrite rules.
wp rewrite list --format=table | head -30

# Check if .htaccess is writable.
ls -la .htaccess

# Regenerate .htaccess.
wp rewrite structure '/%postname%/'

Common causes:

  • Stale rewrite rules (always flush after registering custom post types or taxonomies)
  • .htaccess not writable by the web server
  • Conflicting rewrite rules from multiple plugins
  • register_post_type() called too late (must fire on init)
  • Missing 'publicly_queryable' => true on custom post type

2.8 Memory Exhaustion

Symptom: Fatal error: Allowed memory size of X bytes exhausted in the error log.

// Increase WordPress memory limit in wp-config.php.
define( 'WP_MEMORY_LIMIT', '256M' );       // Frontend.
define( 'WP_MAX_MEMORY_LIMIT', '512M' );   // Admin/backend.
# Check current PHP memory limit.
wp eval 'echo "PHP: " . ini_get("memory_limit") . " | WP: " . WP_MEMORY_LIMIT . PHP_EOL;'

# Find what is consuming memory — add to mu-plugin temporarily.
wp eval '
echo "Memory at load: " . round( memory_get_usage() / 1024 / 1024, 2 ) . "MB" . PHP_EOL;
echo "Peak memory:    " . round( memory_get_peak_usage() / 1024 / 1024, 2 ) . "MB" . PHP_EOL;
'

Common causes:

  • Loading all posts with no limit ('numberposts' => -1 on a site with 100k posts)
  • Infinite loop in a recursive function or a hook that triggers itself
  • Large image manipulation without increasing memory limit
  • Autoloading massive option values from wp_options

2.9 Cron Issues

Symptom: Scheduled events never fire, fire too often, or are missing.

# List all scheduled cron events.
wp cron event list --format=table

# Check if a specific event is scheduled.
wp eval '
$next = wp_next_scheduled( "my_cron_event" );
echo $next ? "Next run: " . date( "Y-m-d H:i:s", $next ) : "NOT scheduled";
'

# Run all d

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [iwritec0de](https://github.com/iwritec0de)
- **Source:** [iwritec0de/wp-dev](https://github.com/iwritec0de/wp-dev)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.