Problem
The page-level streaming path in packages/server/src/ssr.js (streamingHtmlResponse, ~line 1509) builds <p>error: ${escapeHtml(msg)}</p> for a rejected or throwing streamed Suspense boundary in both dev and prod, leaking the internal error message (a DB error, a stack-derived path) to the client.
This is inconsistent with the framework's own SSR error-isolation policy. render-server.js's defaultSSRErrorTemplate is prod-silent, and the core renderToStream Suspense boundary path (render-server.js ~line 1442-1444) already routes boundary errors through it. The two streaming implementations disagree.
Design / approach
Thread the dev flag into streamingHtmlResponse (the call site in ssrPage has opts.dev) and gate the message: dev surfaces error: <msg>, prod emits '' (a silent isolated boundary), matching defaultSSRErrorTemplate.
Found during the #470 fresh-context review. Pre-existing on main (not a #470 regression). Narrow to trigger: per-component async-render throws are already isolated (render-server.js lines 560-585), so this catch only fires on a top-level boundary-promise rejection or a non-isolated renderToString throw. Deferred to its own focused PR rather than expanding #470's scope.
Acceptance criteria
Problem
The page-level streaming path in
packages/server/src/ssr.js(streamingHtmlResponse, ~line 1509) builds<p>error: ${escapeHtml(msg)}</p>for a rejected or throwing streamed Suspense boundary in both dev and prod, leaking the internal error message (a DB error, a stack-derived path) to the client.This is inconsistent with the framework's own SSR error-isolation policy.
render-server.js'sdefaultSSRErrorTemplateis prod-silent, and the corerenderToStreamSuspense boundary path (render-server.js~line 1442-1444) already routes boundary errors through it. The two streaming implementations disagree.Design / approach
Thread the
devflag intostreamingHtmlResponse(the call site inssrPagehasopts.dev) and gate the message: dev surfaceserror: <msg>, prod emits''(a silent isolated boundary), matchingdefaultSSRErrorTemplate.Found during the #470 fresh-context review. Pre-existing on
main(not a #470 regression). Narrow to trigger: per-component async-render throws are already isolated (render-server.jslines 560-585), so this catch only fires on a top-level boundary-promise rejection or a non-isolatedrenderToStringthrow. Deferred to its own focused PR rather than expanding #470's scope.Acceptance criteria
streamingHtmlResponsegates the streamed-boundary error message ondev(prod emits no message).ssr.jspage streamer andrender-server.jsrenderToStream) are consistent (both prod-silent).createRequestHandlerin prod vs dev, asserting the streamed body OMITS the message in prod and INCLUDES it in dev.