Skip to content
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

Thrown object in RSC to be shown as stringified text not [Object object] in client side (next.js) #28312

Conversation

coffeecupjapan
Copy link

This is my first PR for react and I have completed CLA.

Summary

error.message passed from a server component that throws object (normally Error) only displays [object Object] in client side.
see issue in Next.js below.
vercel/next.js#61902

So I add conditional branch in line 1680 of react/packages/react-server/src/ReactFlightServer.js so that not only thrown Error but also object can be stringified.

I am not sure how many people throw object (not Error, usually Error is better since Error can trace stacks) in server component in production, but it would help one user with the issue above.

How did you test this change?

Clone https://github.com/carlos-dubon/nextjs-error-bug and yarn install. ( don't forget to install next 14.1.1-canary.48 and react 18.2.0 and react-dom 18.2.0 ).
Then beatify node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js and edit line 14534.

n instanceof Error ? (a = String(n.message), i = String(n.stack)) : typeof n == "object" ? a = n.message : a = "Error: " + n
スクリーンショット 2024-02-13 19 30 13

after that yarn dev and go to / page and, edit line 10 of this file to

  return <div>Error: {JSON.stringify(error.message)}</div>;

and you can get the message.

Test cases yarn test ReactServer and yarn test --prod ReactServer was passed without fail.

@react-sizebot
Copy link

Comparing: 269edb8...a8c57be

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.min.js = 176.82 kB 176.82 kB = 55.12 kB 55.12 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js = 178.81 kB 178.81 kB = 55.69 kB 55.69 kB
facebook-www/ReactDOM-prod.classic.js = 593.39 kB 593.39 kB = 104.69 kB 104.69 kB
facebook-www/ReactDOM-prod.modern.js = 577.17 kB 577.17 kB = 101.78 kB 101.78 kB
test_utils/ReactAllWarnings.js Deleted 67.02 kB 0.00 kB Deleted 16.42 kB 0.00 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable-semver/react-server/cjs/react-server-flight.development.js +0.26% 66.90 kB 67.08 kB +0.11% 16.31 kB 16.33 kB
oss-stable/react-server/cjs/react-server-flight.development.js +0.26% 66.90 kB 67.08 kB +0.11% 16.31 kB 16.33 kB
oss-experimental/react-server/cjs/react-server-flight.development.js +0.23% 76.60 kB 76.77 kB +0.11% 18.29 kB 18.31 kB
facebook-www/ReactFlightDOMServer-dev.modern.js +0.22% 84.81 kB 85.00 kB +0.11% 18.36 kB 18.38 kB
test_utils/ReactAllWarnings.js Deleted 67.02 kB 0.00 kB Deleted 16.42 kB 0.00 kB

Generated by 🚫 dangerJS against a8c57be

@sebmarkbage
Copy link
Collaborator

There's nothing to indicate that this is meant as an error just because it has a .message property. In fact it can be really confusing to instead just see a partial of the object. I haven't really seen this being a common enough pattern worth supporting.

If anything we could serialize more of the properties optimistically, perhaps as part of the error message which is then turned into an Error object on the client. Like we do with describeObjectForErrorMessage for logged errors.

This all only affects DEV anyway.

@carlos-dubon
Copy link

@sebmarkbage It is a common pattern. See https://stackoverflow.com/questions/41102060/typescript-extending-error-class

We extend the error object and throw it as a normal error. This worked fine before Next.js adopted RSC. Now it's broken. We can't seem to get useful error messages when users report with screenshots.. All we see in our logs is [Object object]

@sebmarkbage
Copy link
Collaborator

@carlos-dubon All the answers in the linked SO use a proper prototype chain which would return true for error instanceof Error so I'm guessing you're doing something different than the suggested answers.

Like I said, we could stringify a shallow JSON representation of the error but that doesn't make it a good idea to not do a proper Error.

@sebmarkbage
Copy link
Collaborator

Thanks for flagging. I'm going to land #28327 instead for now but that one too really needs to expand the context that it provides.

sebmarkbage added a commit that referenced this pull request Feb 14, 2024
)

Also deals with symbols. Alternative to #28312.

We currently always normalize rejections or thrown values into `Error`
objects. Partly because in prod it'll be an error object and you
shouldn't fork behavior on knowing the value outside a digest. We might
want to even make the message always opaque to avoid being tempted and
then discover in prod that it doesn't work.

However, we do include the message in DEV.

If this is a non-Error object we don't know what the properties mean.
Ofc, we don't want to include too much information in the rendered
string, so we use the general `describeObjectForErrorMessage` helper.
Unfortunately it's pretty conservative about emitting values so it's
likely to exclude any embedded string atm. Could potentially expand it a
bit.

We could in theory try to serialize as much as possible and re-throw the
actual object to allow for inspection to be expanded inside devtools
which is what I plan on for consoles, but since we're normalizing to an
Error this is in conflict with that approach.
github-actions bot pushed a commit that referenced this pull request Feb 14, 2024
)

Also deals with symbols. Alternative to #28312.

We currently always normalize rejections or thrown values into `Error`
objects. Partly because in prod it'll be an error object and you
shouldn't fork behavior on knowing the value outside a digest. We might
want to even make the message always opaque to avoid being tempted and
then discover in prod that it doesn't work.

However, we do include the message in DEV.

If this is a non-Error object we don't know what the properties mean.
Ofc, we don't want to include too much information in the rendered
string, so we use the general `describeObjectForErrorMessage` helper.
Unfortunately it's pretty conservative about emitting values so it's
likely to exclude any embedded string atm. Could potentially expand it a
bit.

We could in theory try to serialize as much as possible and re-throw the
actual object to allow for inspection to be expanded inside devtools
which is what I plan on for consoles, but since we're normalizing to an
Error this is in conflict with that approach.

DiffTrain build for [a7144f2](a7144f2)
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
…ebook#28327)

Also deals with symbols. Alternative to facebook#28312.

We currently always normalize rejections or thrown values into `Error`
objects. Partly because in prod it'll be an error object and you
shouldn't fork behavior on knowing the value outside a digest. We might
want to even make the message always opaque to avoid being tempted and
then discover in prod that it doesn't work.

However, we do include the message in DEV.

If this is a non-Error object we don't know what the properties mean.
Ofc, we don't want to include too much information in the rendered
string, so we use the general `describeObjectForErrorMessage` helper.
Unfortunately it's pretty conservative about emitting values so it's
likely to exclude any embedded string atm. Could potentially expand it a
bit.

We could in theory try to serialize as much as possible and re-throw the
actual object to allow for inspection to be expanded inside devtools
which is what I plan on for consoles, but since we're normalizing to an
Error this is in conflict with that approach.
bigfootjon pushed a commit that referenced this pull request Apr 18, 2024
)

Also deals with symbols. Alternative to #28312.

We currently always normalize rejections or thrown values into `Error`
objects. Partly because in prod it'll be an error object and you
shouldn't fork behavior on knowing the value outside a digest. We might
want to even make the message always opaque to avoid being tempted and
then discover in prod that it doesn't work.

However, we do include the message in DEV.

If this is a non-Error object we don't know what the properties mean.
Ofc, we don't want to include too much information in the rendered
string, so we use the general `describeObjectForErrorMessage` helper.
Unfortunately it's pretty conservative about emitting values so it's
likely to exclude any embedded string atm. Could potentially expand it a
bit.

We could in theory try to serialize as much as possible and re-throw the
actual object to allow for inspection to be expanded inside devtools
which is what I plan on for consoles, but since we're normalizing to an
Error this is in conflict with that approach.

DiffTrain build for commit a7144f2.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants