Skip to content

fix: inline seroval and seroval-plugins in dev SSR - #397

Merged
ryansolid merged 1 commit into
solidjs:nextfrom
brenelz:fix/ssr-seroval-single-instance
Oct 8, 2026
Merged

ryansolid merged 1 commit into
solidjs:nextfrom
brenelz:fix/ssr-seroval-single-instance

Conversation

@brenelz

@brenelz brenelz commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Problem

With serverFunctions: { components: true } in SSR start mode, a server component that resolves after the shell flushes (for example one that awaits before returning) fails under vite dev:

[SSR_RENDER_ERROR_CONTAINED] Render error in a <Loading> boundary — the fragment rejected and the client re-renders it:
Error: Seroval caught an error during the parsing process.
The value [object Object] of type "object" cannot be parsed/serialized.

The <Loading> boundary contains the error, so the component silently renders only on the client. Production builds are fine.

Cause

Two copies of seroval load in dev SSR:

  • The inlined @solidjs/web gets seroval from the module runner, which resolves with the development condition, so it loads dist/dev/index.js.
  • seroval-plugins is externalized, so Node loads it and resolves its own import "seroval" without development, which loads dist/index.js.

Seroval detects streams with value instanceof Stream. ReadableStreamPlugin builds its Stream with one copy and the serializer checks it against the other, so the check fails. A server component hits this through its sc:live channel, a ReadableStream serialized into the document. I confirmed the two copies by logging each module evaluation and tracing the rejected value back to sc:live.

This is the same split the plugin already handles for solid-js / @solidjs/web (the comment above the existing inline list describes it).

Fix

Add seroval and seroval-plugins to the dev SSR inline list next to solid-js and @solidjs/web. The existing guards still apply: dev only, Vitest projects and hosts that set noExternal: true are left alone.

Testing

  • New config-level test: examples/start-ssr/test/ssr-inline.mjs, in the style of dedupe.mjs, wired into the example's test script. It fails without the fix and passes 4/4 with it. dedupe.mjs still passes 8/8.
  • End to end: in an app (Solid rc.14, plugin next.47, Nitro) with an awaiting server component, I swapped in the built dist/esm/index.mjs. With the published plugin the seroval error appears 6 times over 3 requests; with this change it appears 0 times, the component renders in the SSR'd document, and the browser hydrates with no console errors.

The existing frames mode of start-ssr doesn't reproduce the bug: its server components serialize sc:live before the shell flushes. So the new test only checks the config; a runtime regression test would need a server component that resolves after the flush.

🤖 Generated with Claude Code

Under `vite dev` the SSR environment already inlines `solid-js` and
`@solidjs/web` so their `dist/dev` builds load once end to end. `seroval`
and `seroval-plugins` split the same way: both ship a `dist/dev` build
behind the `development` condition, and `@solidjs/web` imports both.
Left external, `seroval-plugins` loads through Node and its own
`import "seroval"` resolves to the prod copy, while the inlined
`@solidjs/web` gets the runner's dev copy.

Seroval detects streams with `instanceof Stream`, so the `Stream` that
`ReadableStreamPlugin` builds from one copy is rejected by the other
copy's serializer. Server components hit this when a component resolves
after the shell flush: its `sc:live` ReadableStream fails with "Seroval
caught an error during the parsing process … cannot be
parsed/serialized", `<Loading>` contains the error, and the component
only renders on the client. Builds bundle one copy and are unaffected.

Adds a config-level test (examples/start-ssr/test/ssr-inline.mjs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Oct 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ae8a8b0

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

This PR includes changesets to release 1 package
Name Type
@solidjs/vite-plugin 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

@pkg-pr-new

pkg-pr-new Bot commented Oct 8, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@solidjs/vite-plugin@397

commit: ae8a8b0

@ryansolid
ryansolid merged commit e647ba3 into solidjs:next Oct 8, 2026
6 checks passed
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