Make WP install/DB checks resilient to post-flush stdout (fixes false-negative "Error connecting to the SQLite database")#3646
Draft
ivan-ottinger wants to merge 1 commit into
Conversation
…-flush stdout
Both functions run a tiny PHP probe that uses ob_start()/ob_clean()/ob_end_flush()
to ensure only a controlled '1' or '0' reaches stdout, then JS compares the
captured text strictly against "1". This breaks for any site whose bootstrap
writes to stdout *after* ob_end_flush() — e.g. PHP shutdown handlers,
register_shutdown_function() callbacks, wp_cron's spawn_cron loopback writes,
or transport noise from wp_remote_post fallbacks (Action Scheduler's async
queue runner is a common trigger).
When the strict comparison fails, callers misinterpret the site as
"not installed" and trigger /wp-admin/install.php?step=2, then misinterpret
the post-install verification as "database disconnected" and surface
"Error connecting to the SQLite database" — a triply-misleading message
when the real cause is a single byte of trailing output.
Fix: wrap the controlled output in a sentinel ('<<PG-RESULT:1:END>>') and
match by .includes() rather than strict equality. Trailing post-flush bytes
no longer corrupt the result.
The legacy-boot.ts file uses different probes and isn't affected.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Symptom
Running
@wp-playground/cli serverwith--wordpress-install-mode=install-from-existing-files-if-neededagainst an imported WordPress site exits with:…on a site whose SQLite database is healthy (
PRAGMA integrity_checkreturnsok,wp_options.siteurlis set, all WordPress tables are present). Switching only the--wordpress-install-modeflag todo-not-attempt-installingboots the same site cleanly, which rules out the site, the file mount, and the SQLite plugin as the cause.Reproducer
We have a real-world Jetpack-backed site backup that reproduces this deterministically: https://linear.app/a8c/issue/PLAYGRD-653/bug-wordpress-install-modeinstall-from-existing-files-if-needed-fails
What I observed in the code
packages/playground/wordpress/src/boot.tshas two functions that each run a small PHP probe withob_start()/ob_clean()/ob_end_flush(), then strictly compare the captured PHP stdout to"1":isWordPressInstalled()— line 511, returnsresult.text === '1'on line 529isDatabaseConnectionValid()— line 639, returnsresult.text === '1'on line 639The user-facing
Error: Error connecting to the SQLite database.is thrown when one of these checks returnsfalse. In the failing reproducer above, that happens on a site where the underlying state we can independently inspect (database integrity, WP options, tables) is fine. So the boolean these functions return doesn't reflect the site state — something about the capturedresult.textmakes the strict-equality check returnfalse.I did not pinpoint exactly what is in
result.textin the failing run.Status across versions
The same
text === "1"lines are present in:@wp-playground/wordpress@3.1.28@wp-playground/wordpress@3.1.33(latest published as of writing)So the symptom is reproducible on both, and there's no parallel fix already shipped.
Proposed fix
Wrap the controlled output in a sentinel and match by
.includes()instead of===. This makes the check tolerant of any extra bytes that may end up alongside the'1'/'0'.Both call sites get the same change. The
-1(file-not-found) branch is also wrapped to keep the protocol uniform — both existing callers only check the boolean return, so behavior is unchanged for the negative case.packages/playground/wordpress/src/legacy-wp/legacy-boot.tsuses different probes and is unaffected.Verification
Patched a local copy of
@wp-playground/wordpresswith this diff and re-ran the failing reproducer above. With the patch applied, the wp server starts cleanly and the site is reachable. Without the patch, the same command fails as described in the symptom section.