Skip to content

Surface PHP errors when a site fails to start#3813

Draft
bcotrim wants to merge 1 commit into
trunkfrom
stu-1757-surface-site-start-errors
Draft

Surface PHP errors when a site fails to start#3813
bcotrim wants to merge 1 commit into
trunkfrom
stu-1757-surface-site-start-errors

Conversation

@bcotrim

@bcotrim bcotrim commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Related issues

How AI was used in this PR

Built with Claude Code: it traced the error-reporting flow, reproduced the failure against plain Playground CLI to isolate root causes, implemented the fix, and verified it end-to-end (CLI before/after on a broken site). All code human-reviewed; the empirical findings below were verified with live runs, not just code reading.

Proposed Changes

When a site fails to start (e.g. an active plugin or theme fatals while WordPress loads), Studio only reported WordPress server process exited unexpectedly — with the default debug settings the actual PHP error was recorded nowhere, leaving users (and support) with no way to diagnose it. Three compounding causes:

  1. Playground CLI's runCLI() swallows boot errors and process.exit(1)s (upstream: runCLI() swallows errors via process.exit(1) instead of re-throwing WordPress/wordpress-playground#3520), so Studio's IPC error path never runs.
  2. Playground prints the failure reason to stdout, while the daemon's crash tail only captured stderr (which is empty in this scenario).
  3. With the debug-log setting off, Studio defines WP_DEBUG_LOG=false and Playground's platform mu-plugin then disables log_errors entirely — boot fatals vanish.

With this change:

  • PHP errors are always captured to a Studio-managed wp-content/studio-error.log while the user's debug-log setting is off (their debug.log/WP_DEBUG toggles keep their exact meaning). The file is cleared on each start and excluded from exports and sync pushes.
  • Start failures now append the PHP errors recorded during that attempt (warning + fatal + stack trace) and the log path to the error. Both the CLI and the desktop app benefit, since the error flows through the same IPC path.
  • The daemon also keeps Playground's stdout Error: line in the crash tail, so non-PHP boot failures (port, mounts) carry a reason too.
  • When the debug-log setting is on, the failure detail is read from debug.log instead (gated by mtime so stale entries from previous runs are not shown).

Known limits: fatals in user wp-content/mu-plugins/ or wp-config.php fire before the capture mu-plugin loads (the stdout Error: line still shows); running daemons pick up the stdout-tail change only after restart. The issue title asks for a --verbose flag — capturing errors fixes the underlying need for both the app and CLI surfaces; a --verbose streaming view can build on this as a follow-up.

Testing Instructions

Isolate config + daemon so your real sites are untouched:

DEMO=/tmp/stu1757-demo && mkdir -p $DEMO/site
alias demo-studio='env DEV_CONFIG_DIR=$DEMO/config STUDIO_PROCESS_MANAGER_HOME=$DEMO/daemon node apps/cli/dist/cli/main.mjs'

npm run cli:build
demo-studio site create --path $DEMO/site --name "Demo" --skip-browser
demo-studio site stop --path $DEMO/site
echo 'require_once "this-plugin-is-broken.php";' >> $DEMO/site/wp-content/themes/twentytwentyfive/functions.php
demo-studio site start --path $DEMO/site --skip-browser

Before (trunk):

✖ Failed to start WordPress server: WordPress server process exited unexpectedly

After (this branch):

✖ Failed to start WordPress server: WordPress server process exited unexpectedly
Error: PHP.run() failed with exit code 255.
Recent PHP errors (/tmp/stu1757-demo/site/wp-content/studio-error.log):
[...] PHP Fatal error:  Uncaught Error: Failed opening required 'this-plugin-is-broken.php' (...) in /wordpress/wp-content/themes/twentytwentyfive/functions.php:160
Stack trace:
#0 /wordpress/wp-settings.php(739): include()
...

To compare builds on the same site, recycle the isolated daemon between them (lsof -t $DEMO/daemon/daemon.sock | xargs kill) so the new daemon code is used. Cleanup: kill the daemon the same way, then rm -rf $DEMO.

Also verify the happy path: remove the broken line (sed -i '' '$ d' .../functions.php), start again — site boots normally and no studio-error.log is left behind. For the desktop app, break a throwaway site the same way and hit Start — the error dialog and studio-<date>.log now include the fatal.

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?

🤖 Generated with Claude Code

@bcotrim bcotrim changed the title Surface PHP errors when a site fails to start (STU-1757) Surface PHP errors when a site fails to start Jun 12, 2026
@wpmobilebot

Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing d53d08b vs trunk

app-size

Metric trunk d53d08b Diff Change
App Size (Mac) 1354.74 MB 1354.74 MB +0.00 MB ⚪ 0.0%

site-editor

Metric trunk d53d08b Diff Change
load 1767 ms 1757 ms 10 ms ⚪ 0.0%

site-startup

Metric trunk d53d08b Diff Change
siteCreation 9082 ms 9550 ms +468 ms 🔴 5.2%
siteStartup 4384 ms 4878 ms +494 ms 🔴 11.3%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: add verbose mode to "studio site start" to better report start up errors

2 participants