fix(core): don't treat transient setup-probe DB errors as a fresh install#1345
Conversation
The anonymous fast-path setup probe in the Astro middleware queries `_emdash_migrations` to detect a fresh, un-migrated database. Its catch block treated every error as "fresh install" and redirected to `/_emdash/admin/setup`, so a transient DB failure (D1 connection loss, replica unavailable, query timeout, cold-start race, locked SQLite) wrongly bounced real visitors on a set-up site to the setup wizard. Only redirect when the error is a genuinely-missing table (via the shared `isMissingTableError` helper); otherwise log and render the page. Also move the `setupVerified` flag onto a globalThis Symbol.for singleton so it isn't duplicated across SSR chunks — a plain module-scoped `let` became multiple variables, making the probe re-run far more often than intended (and each re-run was another chance to hit the bug). Adds regression tests: a missing migrations table still redirects to setup; a transient probe error renders the page and does not redirect.
🦋 Changeset detectedLatest commit: 4ae2f2d The changes in this PR will be included in the next version bump. This PR includes changesets to release 14 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
docs | 4ae2f2d | Jun 04 2026, 08:02 PM |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/auth-atproto
@emdash-cms/blocks
@emdash-cms/cloudflare
@emdash-cms/contentful-to-portable-text
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/plugin-cli
@emdash-cms/plugin-types
@emdash-cms/registry-client
@emdash-cms/registry-lexicons
@emdash-cms/sandbox-workerd
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-field-kit
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-demo-cache | 4ae2f2d | Jun 04 2026, 08:05 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ❌ Deployment failed View logs |
emdash-playground | 4ae2f2d | Jun 04 2026, 08:05 PM |
There was a problem hiding this comment.
This PR correctly fixes a real bug where transient database errors in the anonymous setup probe were being misread as "fresh install," causing visitors on fully-set-up sites to be redirected to /_emdash/admin/setup.
Approach: Sound. The author correctly identified that the bare catch block was the only place in the codebase not using the dialect-aware isMissingTableError() helper to distinguish a missing table from other failures. Limiting the redirect to genuinely-missing-table errors, and logging everything else as non-fatal, is the right behavior for a production site.
Global singleton: Moving setupVerified from a module-scoped let onto a globalThis Symbol.for singleton matches the established pattern in request-cache.ts, settings/index.ts, and other core modules. This avoids Vite SSR chunk duplication that was making the probe re-run more often than intended. The implementation (setupFlagStore[SETUP_VERIFIED_KEY] === true) is safe and consistent with siblings.
Tests: The regression test in middleware-prerender.test.ts is focused — it confirms that a missing table still redirects and that a D1 connection-lost error no longer does. The resetSetupVerified() helper in beforeEach correctly resets the global singleton between test runs.
Conventions: I checked for AGENTS.md violations — no new SQL queries (no injection risk), no content-table queries (no locale-filter concern), no admin UI strings (no Lingui or RTL issue). A changeset is present.
Code quality: The diff is focused and minimal. I found nothing worth flagging. LGTM.
What does this PR do?
Fixes logged-in and anonymous visitors being redirected to
/_emdash/admin/setupwhen viewing ordinary frontend pages (e.g. a category page) on a fully set-up site.The anonymous fast-path "setup probe" in
packages/core/src/astro/middleware.tsqueries_emdash_migrationsto detect a fresh, un-migrated database. Itscatchblock treated every error as "fresh install":On a deployed site the migrations table exists, but that probe query can still fail transiently — D1 "Network connection lost"/
D1_ERROR, a read replica being briefly unavailable, a query timeout, a cold-start race before the binding is ready, or a locked SQLite DB. Any of those was misread as "no database yet" and bounced a real visitor to the setup wizard.This is the only place in the codebase that redirected on any DB error. Everywhere else (
middleware/setup.ts,typegen.ts,query.ts,loader.ts, handlers, taxonomies, bylines) uses the dialect-awareisMissingTableError()helper to distinguish a genuinely-missing table from other failures.Changes:
isMissingTableError(error)is true. Other errors are logged (non-fatal) and the page renders normally.setupVerifiedflag is moved from a module-scopedletonto aglobalThisSymbol.for("emdash:setup-verified")singleton, matching the pattern inrequest-cache.ts. Per AGENTS.md, a plain module-scopedletgets duplicated across SSR chunks, which made the probe re-run far more often than intended — and each re-run was another chance to hit the bug.(Logged-in users normally skip this path, but fall into it when the network-backed session lookup transiently returns null, which is why the report noted logged-in users specifically.)
Closes #
Type of change
Checklist
pnpm typecheckpasses (core typechecks clean after buildingregistry-client; the pre-existing@emdash-cms/registry-client/envresolution failures onmainare unrelated to this change)pnpm lintpasses (pnpm lint:jsonreports 0 diagnostics)pnpm testpasses (or targeted tests for my change) — rantests/unit/astro/+tests/unit/middleware/, 96 passedpnpm formathas been runAI-generated code disclosure
Screenshots / test output
Regression test added to
tests/unit/astro/middleware-prerender.test.ts:catch, passes with the fixVerified the transient-error test fails on the pre-fix code (bare
catchredirects) and passes after the fix, while the missing-table test passes in both — confirming the test is specific to the bug.