Why Xdebug Pretty Errors Don’t Work in WordPress (And How to Fix It)

If you’ve ever worked with both vanilla PHP and WordPress in Docker environments, you may have noticed something frustrating: Xdebug’s beautiful error pages work perfectly in plain PHP but disappear in WordPress. Instead of getting Xdebug’s detailed stack traces with syntax highlighting and variable inspection, you’re stuck with WordPress’s basic white screen of death or simple error messages.

This is expected, it’s WordPress doing its job for production safety. Here’s why this happens and how to fix it.

The Problem: WordPress vs Xdebug Error Handling

What You Get in Plain PHP

When Xdebug is properly configured in a vanilla PHP environment, fatal errors and exceptions display as:

  • Detailed stack traces with file previews
  • Syntax-highlighted code around the error line
  • Variable values and local scope inspection
  • Clean, readable formatting with expandable sections

What You Get in WordPress

The same Xdebug configuration in WordPress typically shows:

  • Basic WordPress error messages
  • White screen of death
  • Generic “There has been a critical error” messages
  • No stack traces or debugging information

Why WordPress Blocks Xdebug Pretty Errors

WordPress 5.2+ registers a shutdown handler that renders its own minimal PHP-error template for fatal errors, which replaces Xdebug’s HTML trace output. This Fatal Error Recovery Mode prioritizes user safety and security over developer debugging.

Additionally, WordPress always suppresses on-screen errors for REST, Ajax, and XML-RPC requests regardless of your debug flags — you’ll need to check logs or use your IDE debugger for those (though step debugging still works fine for these requests).

Enable Errors in WordPress

The most effective approach is to configure WordPress for development-friendly error display. Add these configurations to your wp-config.php before the /* That's all, stop editing! */ line:

// Environment & debugging
if (!defined('WP_ENVIRONMENT_TYPE')) {
    define('WP_ENVIRONMENT_TYPE', 'development');
}
define('WP_DEBUG', true);
define('WP_DEBUG_DISPLAY', true);
define('WP_DEBUG_LOG', false); // avoid noisy debug.log in dev if you prefer on-screen

// Let PHP/Xdebug render HTML errors
ini_set('display_errors', '1');
ini_set('html_errors', '1');

// Disable WP's fatal error recovery screen so Xdebug handles fatals
define('WP_DISABLE_FATAL_ERROR_HANDLER', true);

// Note: WordPress never shows errors inline for REST, Ajax, XML-RPC; use your IDE debugger or logs.

Important note: WordPress never shows errors inline for REST, Ajax, XML-RPC requests; check logs or your IDE debugger for those.

Key Configuration Explained

  • WP_DISABLE_FATAL_ERROR_HANDLER: This is the most important setting—it prevents WordPress from intercepting fatal errors
  • html_errors = 1: Recommended for readable HTML output from Xdebug
  • WP_DEBUG_DISPLAY = true: Forces WordPress to show errors instead of logging them
  • The guard around WP_ENVIRONMENT_TYPE prevents “already defined” warnings in some toolchains

Enable Xdebug’s HTML Traces

Configure Xdebug to display detailed error information. In your xdebug.ini:

[xdebug]
xdebug.mode=develop,debug
xdebug.start_with_request=trigger   ; or "yes" for always-on
xdebug.discover_client_host=1
xdebug.client_port=9003

; Readability
xdebug.show_error_trace=1
xdebug.show_exception_trace=1
xdebug.show_local_vars=1
; Optional, use as a last resort to show errors even if the app disables display_errors:
; xdebug.force_display_errors=1

; Tuning
xdebug.max_stack_frames=-1
xdebug.max_nesting_level=1000
xdebug.var_display_max_depth=10
xdebug.var_display_max_children=256
xdebug.var_display_max_data=2048

Configuration Breakdown

  • xdebug.show_error_trace=1: Ensures stack traces appear for fatal errors
  • xdebug.show_exception_trace=1: Shows traces for uncaught exceptions
  • xdebug.show_local_vars=1: Displays variable values in error output
  • xdebug.force_display_errors=1: Use as a last resort to show errors even if the app disables display_errors
  • xdebug.start_with_request=trigger: Prevents connection timeouts when your IDE isn’t listening (use XDEBUG_TRIGGER=1 via cookie/GET/POST/ENV to activate)
  • The var_display_* settings control how much detail Xdebug shows in variable dumps

Docker Specifics

For Docker-based WordPress development, ensure your environment supports Xdebug properly across different operating systems:

Docker Compose Configuration

services:
  wordpress:
    environment:
      - XDEBUG_MODE=develop,debug
      - XDEBUG_CONFIG=client_host=host.docker.internal client_port=9003
    extra_hosts:
      - "host.docker.internal:host-gateway"   # required for Linux compatibility
    volumes:
      - ./config/php/xdebug.ini:/usr/local/etc/php/conf.d/xdebug.ini
      # Mount wp-config.php after initial install (or bake it into your image)
      - ./config/wp-config.php:/var/www/html/wp-config.php

Important Docker Considerations

Linux Compatibility: host.docker.internal works on Mac/Windows but not always on native Linux. The extra_hosts: host.docker.internal:host-gateway configuration maps to Docker’s host gateway so the hostname resolves consistently across platforms.

PHP-FPM Environment Variables: If you’re using PHP-FPM, remember that clear_env = yes is the default (on). Set clear_env = no in your FPM configuration if you rely on XDEBUG_MODE/XDEBUG_CONFIG environment variables from Docker Compose.

Config File Timing: If your container creates wp-config.php on first boot, mount your custom configuration only after initial install or bake your config into a custom image.

Triggering Xdebug: When using start_with_request=trigger, use XDEBUG_TRIGGER=1 (via environment variable, GET/POST parameter, or cookie) to start debugging features.

Advanced Troubleshooting

Verification Steps


Check Xdebug Installation:

<?php 
phpinfo();
// Look for the Xdebug section and verify xdebug.mode includes "develop"
?>


Dump Xdebug Configuration:

<?php
// This shows Xdebug's live config and connection attempts
xdebug_info();
?>


Test Error Display:

<?php
// Create a fatal error to test
call_to_undefined_function();
?>


Verify WordPress Settings:

<?php
var_dump(WP_DEBUG);
var_dump(defined('WP_DISABLE_FATAL_ERROR_HANDLER'));
var_dump(ini_get('display_errors'));
var_dump(ini_get('html_errors'));
?>

Common Issues and Fixes

Problem: Still seeing WordPress error pages
Solution: Ensure WP_DISABLE_FATAL_ERROR_HANDLER is defined before WordPress loads

Problem: Xdebug errors appear but without formatting
Solution: Verify html_errors = 1 in PHP configuration

Problem: No stack traces in error output
Solution: Check that xdebug.show_error_trace=1 is set

Problem: REST/Ajax errors don’t show traces
Solution: This is expected behavior — WordPress always suppresses on-screen errors for these requests. Use your IDE’s step debugger or check logs instead

Best Practices

Development vs Production

Never apply these configurations in production environments:

// Development only!
if (defined('WP_ENVIRONMENT_TYPE') && WP_ENVIRONMENT_TYPE === 'development') {
    define('WP_DISABLE_FATAL_ERROR_HANDLER', true);
    ini_set('display_errors', 1);
    ini_set('html_errors', 1);
}

Environment-Based Xdebug

Consider environment-based Xdebug activation using the XDEBUG_MODE environment variable, which Xdebug recognizes and uses to override the xdebug.mode setting:

; xdebug.ini - keep settings static in the file
xdebug.mode=develop,debug
xdebug.start_with_request=trigger
# docker-compose.yml - control via environment
environment:
  - XDEBUG_MODE=develop,debug  # overrides xdebug.mode setting
  - XDEBUG_CONFIG=client_host=host.docker.internal client_port=9003

Note: xdebug.start_with_request cannot be set via XDEBUG_CONFIG or environment variables, so it must be configured in the INI file.

Team Consistency

Document these settings in your project’s README and ensure all team members use the same configuration for consistent debugging experiences.

WordPress’s Fatal Error Recovery Mode prioritizes user safety over developer debugging, which conflicts with Xdebug’s detailed error reporting. By understanding this conflict and properly configuring both WordPress and Xdebug, you can restore the beautiful error pages that make debugging so much more efficient.

The key is disabling WordPress’s fatal error handler while ensuring Xdebug has permission to display HTML-formatted errors. With these configurations in place, you’ll get the same rich debugging experience in WordPress that you enjoy in vanilla PHP environments.

Remember to keep these settings development-only and never deploy them to production where user-friendly error handling is essential for security and user experience.


This configuration works particularly well in Docker-based development environments where you have full control over the PHP and WordPress configuration files.