Skip to content

Surface PHP errors when a site fails to start - #3813

Merged
bcotrim merged 12 commits into
trunkfrom
stu-1757-surface-site-start-errors
Jun 16, 2026
Merged

Surface PHP errors when a site fails to start#3813
bcotrim merged 12 commits 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. All code human-reviewed; the findings 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 during WordPress load), Studio only showed WordPress server process exited unexpectedly — the actual PHP error was recorded nowhere, so there was no way to diagnose it. (WordPress's WP_DEBUG_LOG is disabled too early in boot to catch a boot fatal, and Playground CLI swallows the error.)

Now Studio captures PHP errors during start via a small mu-plugin and appends them (warning + fatal + stack trace) to the failure message — in the CLI and the desktop app alike. The capture writes to the site's debug.log when the debug-log setting is on, or an internal studio-error.log when off (cleared each start, excluded from exports/sync). It defers to any logging the user already configured, so a custom WP_DEBUG_LOG is left untouched.

Out of scope: fatals in the user's own wp-content/mu-plugins/ or wp-config.php fire before the capture loads. The --verbose flag from the issue title can build on this later.

Testing Instructions

Create a throwaway site and make it fail to boot — easiest in the desktop app:

  1. Create a new site, then stop it.
  2. Append a fatal to its active theme — add require_once 'nope.php'; to wp-content/themes/<active-theme>/functions.php.
  3. Start it again. The failure now includes the PHP fatal + stack trace (on trunk it was just the bare WordPress server process exited unexpectedly).

Or via the CLI (build first with npm run cli:build):

node apps/cli/dist/cli/main.mjs site create --path ~/Studio/stu1757-demo --name "STU-1757 demo" --skip-browser
node apps/cli/dist/cli/main.mjs site stop --path ~/Studio/stu1757-demo
echo "require_once 'nope.php';" >> ~/Studio/stu1757-demo/wp-content/themes/twentytwentyfive/functions.php
node apps/cli/dist/cli/main.mjs site start --path ~/Studio/stu1757-demo --skip-browser

Expected:

✖ Failed to start WordPress server: WordPress server process exited unexpectedly
Recent PHP errors (~/Studio/stu1757-demo/wp-content/studio-error.log):
[...] PHP Fatal error:  Uncaught Error: Failed opening required 'nope.php' (...) in .../themes/twentytwentyfive/functions.php:160
Stack trace:
#0 /wordpress/wp-settings.php(739): include()
...

Also verify:

  • Happy path — remove the fatal line and start again: the site boots and no studio-error.log is left behind.
  • Debug-log on — enable the site's debug-logging setting and repeat: the failure detail comes from the site's debug.log instead, and runtime errors keep going there.

Cleanup: node apps/cli/dist/cli/main.mjs site delete --path ~/Studio/stu1757-demo (or delete it from the app).

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) Jun 12, 2026
@wpmobilebot

wpmobilebot commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing 9c21596 vs trunk

app-size

Metric trunk 9c21596 Diff Change
App Size (Mac) 2334.13 MB 2334.13 MB +0.00 MB ⚪ 0.0%

site-editor

Metric trunk 9c21596 Diff Change
load 1767 ms 1716 ms 51 ms 🟢 -2.9%

site-startup

Metric trunk 9c21596 Diff Change
siteCreation 8585 ms 8525 ms 60 ms 🟢 -0.7%
siteStartup 3910 ms 3876 ms 34 ms ⚪ 0.0%

Results are median values from multiple test runs.

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

@bcotrim
bcotrim marked this pull request as ready for review June 15, 2026 14:33
@bcotrim
bcotrim requested a review from a team June 15, 2026 14:33

@epeicher epeicher left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @bcotrim, this is a great improvement for debugging and identifying errors! I have tested it, and I can confirm that the changes surface the error and they are logged on either server-error.log or debug.log depending on the setting. I have also confirmed that the UI also returns the error as expected and the error is logged in the Studio log file too.

Changes LGTM!

Before After When enabling debug.log
Image Image Image
@bcotrim
bcotrim merged commit 976a786 into trunk Jun 16, 2026
11 checks passed
@bcotrim
bcotrim deleted the stu-1757-surface-site-start-errors branch June 16, 2026 09:42
@gcsecsey gcsecsey mentioned this pull request Jul 16, 2026
2 tasks
gcsecsey added a commit that referenced this pull request Jul 31, 2026
## Related issues

- Related to STU-1370
- Ships the upstream fix WordPress/wordpress-playground#3478, released
in Playground v3.1.45

## How AI was used in this PR

I used Claude to trace STU-1370 to the upstream `runCLI()`
`process.exit()` behavior, confirm the fix shipped in Playground 3.1.45,
bump the dependencies, and regenerate the lockfile. I reviewed the diff
and verified single-copy resolution of the Playground and PHP-WASM
packages myself. Claude also diagnosed and fixed the Windows-only CLI
E2E failure the bump surfaced (see below), reproducing it locally on
Windows.

## Proposed Changes

Studio runs each site's Playground server in a child process that
imports `runCLI()` from `@wp-playground/cli`. When a site hit a PHP
error during startup, `runCLI()` called `process.exit(1)` before the
error could propagate, which killed the child process, so Studio's error
handling never ran and the real error was lost.

The upstream fix (WordPress/wordpress-playground#3478, in 3.1.45)
removes `process.exit()` from the Playground CLI's library APIs, so
`runCLI()` now throws instead of exiting.

This PR:
- Bumps Playground and PHP-WASM to 3.1.45 to pick up that fix
- This is the prerequisite that lets Studio catch PHP-error start
failures at all. On its own it makes the failure report the real PHP
error (via the existing #3813 handling) instead of tearing the child
process down. The user-facing STU-1370 behavior (the site starting and
serving the error page) is a follow-up PR that depends on this bump.
- Lockfile note: alongside the version bumps, npm deduped
`fast-xml-parser` (a nested duplicate collapsed into a single top-level
copy, within existing ranges) and added `zstddec`, a new dependency of
`@wp-playground/wordpress@3.1.45`.
- Windows import fix: on Windows, importing a backup into a running site
failed to restart the server with `Error connecting to the SQLite
database`. `wp sqlite import` leaves the imported database in WAL
journal mode, and 3.1.45 boots the site through the WASM SQLite driver,
which cannot reopen a WAL-mode database on Windows (WAL needs shared
memory the WASM filesystem can't provide there). After importing, Studio
now converts the database back to rollback (DELETE) journal mode — using
Node's native `node:sqlite`, since PHP-WASM itself can't touch the WAL
database — so Playground can reliably reopen it.

## Testing Instructions

Because 3.1.45 is still inside the npm cooldown window, install locally
with the cooldown bypassed. A plain `npm install` works from 2026-07-18
onward.

- Check out this branch and run `npm install --min-release-age=0`
- Confirm the lockfile resolves a single 3.1.45 copy of every
`@wp-playground/*` / `@php-wasm/*` package, with no nested duplicates
- `npm run typecheck` passes
- `npm run cli:build && node apps/cli/dist/cli/main.mjs --version`
builds and runs
- Smoke test: create and start a local site, confirm it loads and runs
normally — i.e. no regression from the runtime bump
- On Windows, confirm the import fix: `npm run cli:build` then `npm test
-- apps/cli/commands/tests/import.e2e.test.ts --tagsFilter='e2e'
--no-file-parallelism -t 'imports a backup into a running site'`

## Pre-merge Checklist

- [ ] Have you checked for TypeScript, React or other console errors?
- [ ] Confirm the merge date is on or after 2026-07-18 (around 04:13
UTC) so CI can install 3.1.45

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants