Skip to content

feat(web): report request failures met before any render - #3763

Merged
ryansolid merged 7 commits into
solidjs:nextfrom
everton-dgn:feat/request-failure-report
Oct 4, 2026
Merged

ryansolid merged 7 commits into
solidjs:nextfrom
everton-dgn:feat/request-failure-report

Conversation

@everton-dgn

@everton-dgn everton-dgn commented Oct 3, 2026 •

Copy link
Copy Markdown

Summary

Closes #3723.

A failure that fails a request before any render or server function met it (a Start middleware throw, a setup or renderMode module failing) had no public way to reach the server error hook. solidjs/solid-vite-plugin#383 reports it today through reportServerError from solid-js/internal, labelled kind: "render" because ServerErrorSite.kind had nothing else, and repeats failRender's console.error fallback by reading the hook slot off globalThis.

  • reportRequestFailure(error, event) in the server entry of @solidjs/web, next to configureServerErrors: it reports { kind: "request", handling: "failed", event } through reportServerError, then calls console.error(error) only when no ambient hook is registered, the same policy as failRender. The hook's return is ignored, as for every handling: "failed", and the once-per-error ledger applies, so a direct server-function failure already heard as server-function/thrown isn't heard again when it escapes the middleware.
  • "request" added to ServerErrorSite.kind in solid-js, on both the server and the client declaration.
  • A no-op reportRequestFailure in server-mock.ts, as configureServerErrors has, so the client entry exports it too.

I exported it from the package root rather than @solidjs/web/server, which the issue mentions: the package has no ./server subpath today, and configureServerErrors already lives in the root's server build.

How did you test this change?

Five tests in packages/web/test/server/server-error-hook.spec.tsx:

  • the ambient hook hears one request/failed call carrying the event, its return is ignored, and nothing reaches console.error;
  • the same error object reported twice is heard once;
  • a direct server-function call that throws under a request and escapes to the handler is heard once, as server-function/thrown with direct: true, and reportRequestFailure adds no second call;
  • with no hook the failure reaches console.error;
  • a throwing hook is reported on the console and the call returns normally.

They fail on next, where the export doesn't exist.

pnpm build, then:

Suite Result
@solidjs/web client 1131 passed, 1 expected fail
@solidjs/web server 1396 passed, 2 skipped
@solidjs/web hydrate 272 passed
@solidjs/web test-types passes
solid-js 819 passed

Public API changes

  • New reportRequestFailure(error: unknown, event: RequestEvent): void from @solidjs/web (server entry; a no-op on the client).

  • ServerErrorSite.kind (and so ServerErrorContext.kind) gains "request", always with handling: "failed" and event set.

  • Behavior change: the server error hook (the render's onError, or the ambient configureServerErrors hook) now hears { kind: "render", handling: "failed" } for a synchronous throw out of renderToString or out of renderToStream's first pass (and so renderToFrameStream and serverComponentResponse), before the error is rethrown to the caller. Previously these reached the caller without the hook hearing them. A handler that catches the rethrow and calls reportRequestFailure adds nothing (once per error). With no hook, nothing is logged on this road; the rethrow is the caller's. Added by the maintainer on this branch.

Maintainer changes

  • Merged next (with chore(size): keep the size comparison green on fork PRs #3765) into this branch; .github/workflows/size.yml now matches next, replacing this PR's duplicate change.
  • Synchronous render throws reported as render/failed before rethrow, with tests (renderToString, renderToStream, renderToFrameStream, per-render onError, no-hook case, and a genuine pre-render failure still reporting request).
  • "request" documented as a failure that fails the request outside any render or server function the runtime reports.
  • server: renderToString floor cap raised 20.38 → 20.42 KB (+35 B), ledger note in scripts/size/scenarios.js.

Size-Exception: renderToString reports synchronous render throws as render/failed before rethrowing (+35 B), accepted by the maintainer.

Add `reportRequestFailure(error, event)` to the server entry of
`@solidjs/web`, with a no-op in the client view, and `"request"` to
`ServerErrorSite.kind`. A framework's request handler that catches a
failure before any render or server function met it (a middleware
throw) reports it through the ambient server error hook as
`{ kind: "request", handling: "failed", event }`, once per error object,
or to `console.error` when no hook is registered, the same policy as a
render that fails before its shell.

Closes solidjs#3723.
@changeset-bot

changeset-bot Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f7bbd58

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
solid-js Patch
@solidjs/web Patch
@solidjs/diagnostics Patch
@solidjs/element Patch
@solidjs/h Patch
@solidjs/html Patch
test-integration Patch
@solidjs/universal Patch
todos-server-example Patch
@solidjs/babel-plugin Patch
@solidjs/compiler Patch
@solidjs/signals Patch

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

@codspeed

codspeed Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 185 untouched benchmarks
⏩ 3 skipped benchmarks1


Comparing everton-dgn:feat/request-failure-report (f7bbd58) with next (4738c6d)2

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

  2. No successful run was found on next (9338c00) during the generation of this report, so 4738c6d was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩

everton-dgn and others added 6 commits October 3, 2026 20:08
Co-authored-by: Cursor <[email protected]>

# Conflicts:
#	.github/workflows/size.yml
…ethrow

A synchronous throw out of renderToString, or out of renderToStream's
first pass (and so renderToFrameStream and serverComponentResponse),
reached the caller without the server error hook hearing it. A request
handler that caught it and called reportRequestFailure filed it as
kind "request". The render now reports it as render/failed through its
own onError or the ambient hook before rethrowing, so the handler's
report of the same error is deduped by the once-per-error ledger. No
console fallback on this road: the rethrow is the caller's.

The "request" kind is documented as a failure that fails the request
outside any render or server function the runtime reports.

Co-authored-by: Claude via Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
Measured at 20,412 B against next @ 4738c6d's 20,377 (+35 B; +72 B
minified): the catch in renderToString that reports a synchronous throw
as render/failed before rethrowing. Accepted by the maintainer; ledger
note in scripts/size/scenarios.js.

Size-Exception: renderToString reports synchronous render throws as render/failed before rethrowing (+35 B), accepted by the maintainer.

Co-authored-by: Claude via Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
@ryansolid

Copy link
Copy Markdown
Member

Thanks for this, @everton-dgn. The design holds up, and we pushed a few changes on top so it can land:

  • next merged in (ea66b0da3, then f7bbd583a to join your own merge of next). .github/workflows/size.yml now matches next, so the duplicate of chore(size): keep the size comparison green on fork PRs #3765 is gone. Nothing was rewritten or force-pushed.

  • Synchronous render throws are now reported as render/failed before they're rethrown (f2cab7908). This covers renderToString and the first pass of renderToStream (and so renderToFrameStream and serverComponentResponse). Before, those errors reached the caller without the hook hearing them, so a handler that passed them to reportRequestFailure filed a render failure as request. Now that call adds nothing:

    try { renderToString(() => <App />); } catch (err) { reportRequestFailure(err, event); }
    // hook: [{ kind: "render", handling: "failed" }], heard once

    We added 6 tests: renderToString, renderToStream, renderToFrameStream, a per-render onError, the no-hook case, and a genuine middleware failure that still reports request.

  • Docs: the JSDoc, 12-ssr-http.md and the changeset now describe "request" as a failure that fails the request outside any render or server function the runtime reports.

  • Size: the server: renderToString floor cap is raised from 20.38 to 20.42 KB (+35 B for the catch) under a Size-Exception the maintainer accepted (b3f30b438). There's a ledger note in scripts/size/scenarios.js, and the PR body now lists the behaviour change under Public API changes.

All checks are green.

— Claude via Cursor

@ryansolid
ryansolid merged commit 41fdf96 into solidjs:next Oct 4, 2026
7 checks passed
ryansolid added a commit that referenced this pull request Oct 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants