fix: encode X-Error header for server function errors with non-latin1 messages (#1874)#2215
Merged
Conversation
… messages (#1874) A server function throwing an Error whose message contains characters above U+00FF (e.g. "Ошибка 🚀") crashed the request: the raw message was assigned to the X-Error response header, and because header values must be ByteString-safe, Headers.set threw a TypeError inside the catch block. That produced a bare 500 with no X-Error header and no serialized body, so the client never threw the deserialized error and the route section hung empty instead of rendering the ErrorBoundary. Add toHeaderValue() to strip newlines and encodeURIComponent the value only when it contains non-latin1 characters (ASCII messages stay readable), falling back to "true" on the lone-surrogate edge case. Also return the constructed `headers` (which carries X-Error) instead of the discarded `body.headers` in the body-error path, so X-Error propagates consistently with the seroval path. Add an e2e test exercising the server-fn error path with a unicode message. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
✅ Deploy Preview for solid-start-landing-page ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
🦋 Changeset detectedLatest commit: 0d3c937 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
commit: |
birkskyum
approved these changes
Jul 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Closes #1874. When a server function throws an
Errorwhose message contains characters above U+00FF (e.g."Ошибка 🚀"), the request crashes instead of surfacing the error.In
packages/start/src/fns/handler.ts, the catch block assigns the raw error message directly to theX-Errorresponse header. HTTP header values must be ByteString-safe (chars ≤ U+00FF), soHeaders.setthrows aTypeErrorinside the catch block. The result is a bare, unhandled500with noX-Errorheader and no serialized body — so the client (fns/client.ts) never seesX-Error, never throws the deserialized error, and the route section hangs empty instead of rendering theErrorBoundary.Fix
toHeaderValue(): strips newlines (as before), thenencodeURIComponents the value only when it contains non-latin1 characters (ASCII messages stay human-readable), with a fallback to"true"on the lone-surrogate edge case. The client only checksresponse.headers.has("X-Error")— the value itself is unused — so the real error message continues to travel in the seroval-serialized body that the client throws.headers(which carriesX-Error) instead of the discardedbody.headersin the body-error path, soX-Errorpropagates consistently with the seroval path. Previously this variable was built and then thrown away.Testing
apps/tests): a route with a server function that throws a unicode-message error, and a test asserting the message reaches the client via theX-Errorpath. Verified it fails without the fix and passes with it; the fullserver-functione2e suite is green (20/20)./_serverrequest now returns200with an encodedx-errorheader, the client navigates, and theErrorBoundaryrenderscaught: Ошибка 🚀 ünïcode — special chars.🤖 Generated with Claude Code