Skip to content

dogfood: dev error overlay leaks across the gallery after an intentional-throw page #1047

Description

@vivek7405

Problem

In dev, after visiting a page that throws during SSR (the scaffold's app/features/boundaries/crash demo, which intentionally throw new Error('demo: this page threw during render')), the "Server render error" overlay popup then appears randomly across the rest of the gallery, on pages that render fine. It reliably reappears on/around the boundary pages. A hard refresh of a good page clears it; navigating there via the client router does not.

Observed against a scaffolded app (webjs create, Bun runtime) at app/features/boundaries/crash/page.ts.

Design / approach

Two dev-only mechanisms keep a stale error alive, neither cleared by a successful client-router navigation:

  1. The overlay is a body-level injected element (data-webjs-error-overlay, dev-overlay.js renderDevOverlay) dismissed ONLY by its close button or dismissDevOverlay(). It sits outside the client router's swap range, so a soft-nav to a good page leaves it on screen.
  2. The dev SharedWorker retains lastError and re-broadcasts it to every tab and every new port/reconnect (dev-reload-worker.js L55 sets lastError on each webjs-error, L65 re-posts it to every newly-connecting port). So a reconnect or a new tab re-shows the last error even though nothing is currently wrong ("appears randomly").

Fix direction: a successful navigation / successful render should clear the overlay. Have the client router call dismissDevOverlay() after a clean swap (dev builds only), AND have the worker drop lastError once a subsequent successful render or reload supersedes it (or gate re-broadcast on the error still being current). Keep it strictly dev-only (production ships no overlay).

Implementation notes (for the implementing agent)

  • Where to edit:
    • packages/server/src/dev-overlay.js: renderDevOverlay (appends the overlay), dismissDevOverlay (L18). The overlay is body-level.
    • packages/server/src/dev-reload-worker.js: lastError retention (L55) + re-post to new ports (L65). Decide when lastError is stale.
    • packages/server/src/dev.js: __webjsApplyError (~L2807) and the SharedWorker onmessage / __webjsDirectEvents wiring (~L2842-2856). The reload client is assembled here from DEV_OVERLAY_SRC.
    • Client hook: packages/core/src/router-client.js after a successful applySwap (the two-tier boundary swap ~L2818 / frame swap) is where a "clear dev overlay on successful nav" call would live, guarded so it is a no-op in prod (the overlay dismiss fn only exists in the dev reload client, so call it via a global check like window.dismissDevOverlay / a documented dev hook, not a static import from server code).
  • Landmines:
    • Strictly DEV-only. packages/core must not import server/dev code; reach the dismiss via a global the dev reload client installs, or dispatch a documented event the dev client listens for. Do not pull dev-overlay into the production browser bundle.
    • The boundaries/crash demo is an INTENTIONAL uncaught throw (its own comment says expected failures should use ActionResult / notFound() / forbidden()). So the overlay SHOWING on that page is correct; only its LEAK to other pages is the bug. Do not "fix" the demo.
    • Don't clear the overlay on a FAILED navigation (a real error should stay). Only a clean render clears it.
    • dogfood: Node dev full-restarts on every app edit (downtime + CSS flash) #893 context: the worker deliberately retains state to survive restarts and re-show a still-relevant error; the fix must not reintroduce the missing-error-after-restart gap. Distinguish "same error still current" from "superseded by a good render".
  • Invariants: no build step, .js + JSDoc in packages/ (root + packages/core/packages/server AGENTS.md). Dev-overlay behaviour has browser tests already (dev-overlay.js is imported directly by a browser test per dev.js comment ~L2773); extend those.
  • Tests + docs: browser test that applies an error frame, then simulates a successful navigation, and asserts the overlay is gone. Counterfactual: fails before the fix. Docs: the dev error overlay is described in framework-dev.md (dev error overlay section); sync if behaviour changes.

Acceptance criteria

  • After a client-router navigation to a cleanly-rendering page, no stale dev error overlay remains on screen
  • A reconnect / new tab does not resurrect an error that a later successful render superseded
  • The overlay still shows on the page that actually threw, and a genuine current error is not cleared prematurely
  • Strictly dev-only: nothing added to the production browser bundle
  • Browser test covers apply-error then navigate-clean, with a counterfactual

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

Status
Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions