Skip to content

Serve a PHP error page and auto-recover when a site fails to start - #4246

Open
gcsecsey wants to merge 27 commits into
trunkfrom
gcsecsey/stu-1370-php-error-recovery
Open

Serve a PHP error page and auto-recover when a site fails to start#4246
gcsecsey wants to merge 27 commits into
trunkfrom
gcsecsey/stu-1370-php-error-recovery

Conversation

@gcsecsey

@gcsecsey gcsecsey commented Jul 17, 2026

Copy link
Copy Markdown
Member

Related issues

  • Fixes STU-1370
  • Stacked on Bump Playground to 3.1.45 #4230 (the Playground 3.1.45 bump). This branch includes and depends on that bump, since the recovery only works once runCLI() throws instead of calling process.exit().

How AI was used in this PR

I used Claude to port the PHP-error recovery from an earlier branch onto the current trunk, wire it into the start and stop handlers, and add unit tests for the error classification and parsing. I reviewed the logic and verified typecheck, build, and the tests myself.

Proposed Changes

When a site has a fatal PHP error (for example in a theme or a mu-plugin), starting it should not just fail. The site should come up and show the error, the way the native PHP runtime already does.

For the Playground (WASM) runtime, this cannot be done the native way. Playground warm-boots WordPress at startup, so a fatal error aborts the boot and the server never comes up, which means there is no live WordPress to render its own error page. This PR adds a Studio-side recovery that fills that gap:

  • When a start fails with a PHP user error (not an infrastructure error such as a WASM-memory or port problem), Studio serves a lightweight error page on the site's port showing the parsed PHP error, and marks the site as running so it is reachable.
  • A file watcher retries the start when a .php file changes, so fixing the error auto-recovers the site.
  • If the error is introduced while the site is already running, nothing here kicks in. The warm Playground server serves WordPress's own error page per request.

Testing Instructions

  • Check out this branch and run npm install
  • Launch Studio with npm start
  • Regression test: Create a new local WordPress site using the Sandbox environment, start it, and confirm it loads normally in the browser

Case 1 (error at start)

  • Stop the site
  • Create a wp-content/mu-plugins/fatal.php file in the site folder and add:
<?php trigger_error( 'Intentional fatal for testing', E_USER_ERROR );
  • start the site
  • Check that the site comes up and serves an error page showing the PHP error, instead of a failure modal.
  • Fix the error by deleting fatal.php.
  • Check that the file watcher retries and the real site loads automatically.

Case 2 (running site, error at runtime)

  • With the site running, add the same fatal error to a .php file and reload the site in the browser.
  • Check that WordPress's own error page is served by the warm server.
State trunk this branch
Stopped site, start with a PHP error CleanShot 2026-07-30 at 15 32 12@2x CleanShot 2026-07-30 at 15 31 03@2x
Running site, PHP error introduced at runtime CleanShot 2026-07-30 at 15 31 45@2x CleanShot 2026-07-30 at 15 31 45@2x
gcsecsey and others added 20 commits July 17, 2026 11:36
…ayground-fix

# Conflicts:
#	packages/data-liberation-agent/dist/mcp-server.bundle.mjs
#	packages/data-liberation-agent/dist/scripts/_validate.mjs
#	packages/data-liberation-agent/dist/scripts/carry-reconstruct-drive.mjs
#	packages/data-liberation-agent/dist/scripts/chunk-3QFGXLW4.mjs
#	packages/data-liberation-agent/dist/scripts/chunk-5ISMP4W6.mjs
#	packages/data-liberation-agent/dist/scripts/chunk-J2WXJEI3.mjs
#	packages/data-liberation-agent/dist/scripts/chunk-PXH76XU4.mjs
…' into gcsecsey/stu-1370-php-error-recovery

# Conflicts:
#	apps/studio/src/ipc-handlers.ts
… journal mode

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ayground-fix

# Conflicts:
#	packages/data-liberation-agent/dist/mcp-server.bundle.mjs
…ayground-fix

# Conflicts:
#	packages/data-liberation-agent/dist/mcp-server.bundle.mjs
…p-error-recovery

# Conflicts:
#	packages/data-liberation-agent/dist/mcp-server.bundle.mjs
#	packages/data-liberation-agent/dist/scripts/carry-reconstruct-drive.mjs
#	packages/data-liberation-agent/dist/scripts/chunk-JSC2QBHK.mjs
@gcsecsey
gcsecsey requested a review from Copilot July 30, 2026 14:29
@gcsecsey
gcsecsey marked this pull request as ready for review July 30, 2026 14:29
@gcsecsey
gcsecsey requested a review from a team July 30, 2026 14:34

Copilot AI 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.

Pull request overview

Adds a “PHP error recovery” mode to Studio’s Playground (WASM) runtime startup path so a site can still become reachable when WordPress fatals during warm boot, by serving a lightweight error page on the site’s port and auto-retrying startup on .php changes.

Changes:

  • Introduces a recovery server + PHP error parsing/classification, with a file watcher that retries startup after PHP edits.
  • Updates running-state adoption and event propagation so “in recovery” sites remain effectively “running” in Studio despite the CLI reporting them stopped.
  • Adds unit tests for parsing/classification and for running-state reconciliation; adds child-process logging to avoid crashes on stray unhandled errors.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
package-lock.json Lockfile updates from dependency resolution changes.
apps/studio/src/tests/reconcile-running-state.test.ts Adds coverage ensuring recovery-mode sites remain treated as running during reconciliation.
apps/studio/src/site-server.ts Adds inErrorRecovery flag and ensures running-state adoption treats recovery sites as running.
apps/studio/src/modules/cli/lib/cli-events-subscriber.ts Treats recovery sites as effectively running and adjusts emitted IPC site events accordingly.
apps/studio/src/lib/tests/php-error-recovery.test.ts New unit tests for PHP error parsing/classification and recovery server lifecycle.
apps/studio/src/lib/php-error-recovery.ts Implements error-page server, log parsing, recovery state, and file-watcher retry loop.
apps/studio/src/ipc-handlers.ts Wires recovery into startServer/stopServer, serving the recovery page on startup PHP errors and notifying the renderer.
apps/cli/playground-server-child.ts Adds global uncaughtException/unhandledRejection logging to avoid child crashes that would prevent recovery.
Comments suppressed due to low confidence (2)

apps/studio/src/lib/php-error-recovery.ts:155

  • The recovery URL is hardcoded to http://localhost:${port}, which ignores customDomain/enableHttps. For sites using a custom domain, the UI will point at localhost instead of the configured domain, even though the proxy/domain should still be able to reach the port-bound recovery server.
	const url = `http://localhost:${ port }`;

apps/studio/src/lib/php-error-recovery.ts:137

  • If siteServer.start() fails and startErrorRecovery(...) also throws (e.g. the port is still busy, or the watcher/server fails to initialize), the async IIFE rejection is currently unhandled (void (async () => ...)()), which can produce an unhandled rejection and potentially crash/abort the main process. startErrorRecovery should be wrapped so recovery restart failures are handled and logged instead of escaping.
					} catch {
						// Still failing - re-serve the error page with the latest error and keep watching.
						console.log( `[PHP Recovery - ${ id }] Retry failed, still watching...` );
						const pm2Logs = readPm2Logs( id );
						const logContent = [ ...( pm2Logs.stdout ?? [] ), ...( pm2Logs.stderr ?? [] ) ].join(

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apps/studio/src/lib/php-error-recovery.ts Outdated
gcsecsey added 2 commits July 31, 2026 11:54
…ayground-fix

# Conflicts:
#	apps/cli/package.json
#	apps/studio/package.json
#	package-lock.json
gcsecsey added 2 commits July 31, 2026 12:11
…' into gcsecsey/stu-1370-php-error-recovery

# Conflicts:
#	apps/studio/package.json
#	package-lock.json
Base automatically changed from gcsecsey/stu-1370-playground-fix to trunk July 31, 2026 11:48
@wpmobilebot

Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing c6d6380 vs trunk

app-size

Metric trunk c6d6380 Diff Change
App Size (Mac) 1372.96 MB 1373.05 MB +0.09 MB ⚪ 0.0%

site-editor

Metric trunk c6d6380 Diff Change
load 1008 ms 1042 ms +34 ms ⚪ 0.0%

site-startup

Metric trunk c6d6380 Diff Change
siteCreation 6514 ms 6587 ms +73 ms 🔴 1.1%
siteStartup 2386 ms 2380 ms 6 ms ⚪ 0.0%

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

3 participants