Skip to content

feat: add a server function error handler - #2262

Draft
adipascu wants to merge 3 commits into
solidjs:mainfrom
adipascu:feat/server-fn-error-hook
Draft

feat: add a server function error handler#2262
adipascu wants to merge 3 commits into
solidjs:mainfrom
adipascu:feat/server-fn-error-hook

Conversation

@adipascu

@adipascu adipascu commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

PR Checklist

  • Addresses an existing open issue: fixes #000
  • Tests for the changes have been added (for bug fixes / features)

What is the current behavior?

handleServerFunction catches everything a server function throws and writes it straight into the response. Nothing else observes it, so the crash is invisible to monitoring, and whatever was thrown reaches the client as-is.

What is the new behavior?

A new serverFunctions.onError option names a module whose default export is called with the thrown value before it is serialized. Returning a value replaces what is sent, returning undefined changes nothing.

solidStart({ serverFunctions: { onError: "src/server-fn-error.ts" } });

Naming the module in the config rather than registering a handler at runtime keeps the app in sole control of it, so no dependency can reach the running server and take over reporting. It follows serialization.plugins from #1474, with its own virtual module because this one is bundled into the server only and may import a monitoring SDK.

How other frameworks do this:

  • SvelteKit handleError, which observes and replaces: docs (src). Same handler(...) ?? fallback shape as this PR.
  • Next.js onRequestError, observe only: docs (src)
  • React Router handleError, observe only: docs (src)
  • Nitro error hook, observe only: docs (src)
  • TanStack Start has no dedicated hook. Global functionMiddleware wraps every server function, so an app catches around next() itself: docs (src)

Replacing matters for adapters whose runtime wraps failures, such as Effect's FiberFailure. Unwrapping here keeps a Response thrown through the wrapper recognised as control flow by the x instanceof Response branch.

@netlify

netlify Bot commented Jul 28, 2026

Copy link
Copy Markdown

Deploy Preview for solid-start-landing-page ready!

Name Link
🔨 Latest commit 38078e8
🔍 Latest deploy log https://app.netlify.com/projects/solid-start-landing-page/deploys/6a68ee363258aa0007256db4
😎 Deploy Preview https://deploy-preview-2262--solid-start-landing-page.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@changeset-bot

changeset-bot Bot commented Jul 28, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 38078e8

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

This PR includes changesets to release 1 package
Name Type
@solidjs/start Minor

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

@pkg-pr-new

pkg-pr-new Bot commented Jul 28, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@solidjs/start@2262

commit: 38078e8

@adipascu
adipascu force-pushed the feat/server-fn-error-hook branch from f25d012 to acccfe0 Compare July 28, 2026 15:42
Comment thread packages/start/src/fns/handler.ts Outdated
// Nothing else observes a server function failure: it is caught here and
// written straight to the response, so monitoring never sees the crash and
// the thrown value reaches the client as-is.
x = globalThis.__transformServerFnError?.(x) ?? x;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't agree with a globalThis implementation, that can be tampered with by another code (hoping its not malicious actors)

@adipascu adipascu Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, dropped it. The handler is now registered through setServerFunctionErrorHandler, exported from @solidjs/start/fns/server, so nothing reads or writes globalThis.

@adipascu adipascu changed the title feat: allow a server function error to be observed and replaced before serialization feat: add a server function error handler Jul 28, 2026
@adipascu
adipascu force-pushed the feat/server-fn-error-hook branch 4 times, most recently from 4f304fb to 2d14f17 Compare July 28, 2026 17:24
@adipascu
adipascu marked this pull request as ready for review July 28, 2026 17:24
@adipascu
adipascu force-pushed the feat/server-fn-error-hook branch from 2d14f17 to ce68392 Compare July 28, 2026 17:29
Comment thread packages/start/src/fns/error-handler.ts Outdated
export function setServerFunctionErrorHandler(
handler: ServerFunctionErrorHandler | undefined,
): void {
errorHandler = handler;

@lxsmnsyc lxsmnsyc Jul 28, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functionally, I think it's still the same as the last one since another package can just import this function and call it through peer dependency.

I think the go-to solution here is somewhere similar to #1474 , I think we can change that where instead of just exporting the plugins, we can also export something like an onServerFunctionError that the runtime can call.

Example:

// serialization.ts
export function onServerFunctionError(error: unknown) {
  return doStuff(error);
}
import { onServerFunctionError } from 'solid-start:serialization';

// ...
x = onServerFunctionError(err);

@adipascu
adipascu marked this pull request as draft July 28, 2026 18:00

@lxsmnsyc lxsmnsyc left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI works wonders

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