-
Notifications
You must be signed in to change notification settings - Fork 47k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bug: React 18 emit null chars in renderToPipeableStream
#31134
Comments
Just to be clear, you're emitting actual Sadly, I think this is a bug in What we probably should be doing instead is escape |
@slorber can you update to React 19 canary in a branch and push that up? we don't minify production builds there. It's posssible the bug will go away with the upgrade but that's not actually comforting because what is almost certainly happening is there is a character on the boundary of two chunks (2048 bytes) that uses more than one utf-8 code point and so small shifts in the content or chunk size can cause this character to no longer land on a boundary. But if it does repro in 19 it will be easier to track down what is going on. Also is the mdx for the 3.5 available in the repro? It seems embedded in the compiled project so I can't quite see all the input strings unless I'm misinterpretting the repro repository |
Thanks for taking a look Docusaurus isn't using React 19 yet so I'll have to make a POC of upgrade to see if I can reproduce. I'll investigate more at the end of the week. The problematic blog post source is https://github.com/facebook/docusaurus/blob/main/website/blog/releases/3.5/index.mdx I checked and our MDX sources do not contain NULL chars, nor the MDX compiled output. And I tried with both Webpack/Rspack so it's probably not a bundler bug unless they ported it to Rspack. |
I saw this issue in Remix too. Update: they don't contain NUL today just thank to a monkey patch by Docusaurus itself. |
#24985 looks similar to this bug to some extent. |
Yes it looks very similar to this other bug fixed by @sophiebits in #26228 However in our case it doesn't seem to be fixed under v18.3.0 Similarly, I can confirm the bug isn't there on v18.0.0 and appears at 18.1.0 (release: https://github.com/facebook/react/releases/tag/v18.1.0) The change that introduced this problem is probably this @gnoff PR: #24291 |
I can also confirm the bug is not there anymore with |
I wonder which version and commit in the canary or next channel this bug is fixed in if you are correct. |
Oh, #26228 hasn't been backported to v18.x yet! The latest commit in https://github.com/facebook/react/commits/v18.3.1/packages/react-server/src/ReactServerStreamConfigNode.js All we have to fix this problem is to backport #26228 to v18.x. I believe it can never cause a bad regression and is safe for existing users of v18.x. I found a monkey patch for v18.3.1: sed -i -e '11s/\(d<b.length&&(t(a,k\))/\1.subarray(0,l))/' node_modules/react-dom/cjs/react-dom-server.node.production.min.js
sed -i -e '129s/currentView/currentView.subarray(0, writtenBytes)/' node_modules/react-dom/cjs/react-dom-server.node.development.js https://unpkg.com/browse/react-dom@18.3.1/cjs/react-dom-server.node.production.min.js https://unpkg.com/browse/react-dom@18.3.1/cjs/react-dom-server.node.development.js These are equivalent to the patch on Once this monkey patch was applied, NULL characters in my Docusaurus build & Remix app did vanish completely as if they had been enchanted. If you want to apply this patch using your text editor:
Go to the 143rd character in the line 11, and replace
Go to the line 129 and replace |
I'm not sure the React team is planning any new v18.x release anymore considering v19 is around the corner. v18.3 was released only to add extra warnings to help us migrate to v19, and did not contain any bugfix/feature: https://github.com/facebook/react/releases/tag/v18.3.0 I guess we can close as duplicate, already fixed in v19. React 18 users will have to apply the patch locally with |
It's not duplicated but just changed to a backport request to 18.x. It's unhealthy to lead an outsider of Meta to maintain something like https://github.com/tats-u/react-dom-no-nul and lead React 18.x users to use it. |
@tats-u I'm not a Meta employee and not part of the React core team. I have no weight on their decision to release another v18.x. Considering v18.3 was released 2 years after v18.2 with 0 backported bug fixes, my intuition tells me there's no v18.4 release planned. Even before v18.3 they communicated on Twitter no v18.3 was planned. https://react.dev/blog/2024/04/25/react-19-upgrade-guide https://x.com/sebastienlorber/status/1780224019398066433 This was initially a bug report, and the bug has been fixed in v19. For Docusaurus I apply the same strategy and do not necessarily backport fixes to v2 anymore since we are at v3.5 already, even if there are still many v2 sites. It's a tradeoff many of us have to make to move faster and encourage users to upgrade. If you want to open a backport request, feel free to do so in your own name in a different issue. There are decent workarounds such as using |
It's disappointing, but we don't have to use extra packages to patch React if we have adopted pnpm or Yarn v2+. |
React version: 18.3.1
Steps To Reproduce
Replace
renderToString(app)
byrenderToPipeableStream(app)
.The Docusaurus SSG framework own website generates static pages with React, and after the change, some of them will start containing NULL chars
\0
.The current behavior
renderToString(app)
does not emit NULL chars\0
renderToPipeableStream(app) emit NULL chars
\0`The expected behavior
renderToString(app)
does not emit NULL chars\0
renderToPipeableStream(app) does not emit NULL chars
\0`Details
renderToPipeableStream
is not a 1-1 replacement forrenderToString
, but I'm using the following code to obtain a string:I also tried various alternatives suggested by @sebmarkbage, @gnoff and the community in this Twitter thread, including:
new Response(webStream).text()
TextEncoder.decode(chunk,{stream: true})
All the methods I tried to convert
renderToPipeableStream
did have occasional NULL chars, unlikerenderToString
, so I assume it is, in the end, a React bug.See related Docusaurus issue: facebook/docusaurus#9985
Runnable repro
A full repro is provided here: https://github.com/slorber/react-bug-repro-null-chars
It is standalone, but unfortunately not very minimal because it contains the full server bundle we run SSR/SSR with for the Docusaurus website, and also bundles React and ReactDOMServer.
The repro code:
Similarly to the Docusaurus website, only the path
/blog/releases/3.5
contains NULL chars, and the other tested ones do not:This problematic page is an MDX release blog post, and the NULL char occurs in the middle of it.
For some unknown reasons, modifying the beginning of the post (adding or removing Markdown
**
) randomly makes the NULL chars disappear, even if it's far from the NULL occurrence position. Note that the compiled output of MDX doesn't contain NULLs. For these reasons, it might be challenging to strip down the repro and create a more minimal one, although I could try.Follow-up
This is an initial bug report that I plan to complete depending on your answers:
html.replace(/\0/g, '')
is a good/safe temporary workaround?Thanks
Edit: the following method using
renderToReadableStream
behaves as expected (exactly likerenderToString
) and doesn't produce extra NULL chars:So it looks like the problem only affects
renderToPipeableStream
We might use this method in Docusaurus to fix our problem: facebook/docusaurus#10562
The text was updated successfully, but these errors were encountered: