You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
The overlay is a body-level injected element (data-webjs-error-overlay, dev-overlay.jsrenderDevOverlay) 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.
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
Problem
In dev, after visiting a page that throws during SSR (the scaffold's
app/features/boundaries/crashdemo, which intentionallythrow 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) atapp/features/boundaries/crash/page.ts.Design / approach
Two dev-only mechanisms keep a stale error alive, neither cleared by a successful client-router navigation:
data-webjs-error-overlay,dev-overlay.jsrenderDevOverlay) dismissed ONLY by its close button ordismissDevOverlay(). It sits outside the client router's swap range, so a soft-nav to a good page leaves it on screen.lastErrorand re-broadcasts it to every tab and every new port/reconnect (dev-reload-worker.jsL55 setslastErroron eachwebjs-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 droplastErroronce 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)
packages/server/src/dev-overlay.js:renderDevOverlay(appends the overlay),dismissDevOverlay(L18). The overlay is body-level.packages/server/src/dev-reload-worker.js:lastErrorretention (L55) + re-post to new ports (L65). Decide whenlastErroris stale.packages/server/src/dev.js:__webjsApplyError(~L2807) and the SharedWorkeronmessage/__webjsDirectEventswiring (~L2842-2856). The reload client is assembled here fromDEV_OVERLAY_SRC.packages/core/src/router-client.jsafter a successfulapplySwap(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 likewindow.dismissDevOverlay/ a documented dev hook, not a static import from server code).packages/coremust 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.boundaries/crashdemo 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..js+ JSDoc inpackages/(root +packages/core/packages/serverAGENTS.md). Dev-overlay behaviour has browser tests already (dev-overlay.jsis imported directly by a browser test per dev.js comment ~L2773); extend those.framework-dev.md(dev error overlay section); sync if behaviour changes.Acceptance criteria