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

Use a Wrapper Error for onRecoverableError with a "cause" Field for the real Error #28736

Merged
merged 7 commits into from
Apr 4, 2024

Conversation

sebmarkbage
Copy link
Collaborator

We basically have four kinds of recoverable errors:

  • Hydration mismatches.
  • Server errored but client didn't.
  • Hydration render errored but client render didn't (in Root or Suspense boundary).
  • Concurrent render errored but synchronous render didn't.

For the first three we log an additional error that the root or Suspense boundary didn't error. This provides some context about what happened. However, the problem is that for hydration mismatches that's unnecessary extra context that is confusing. We also don't log any additional context for concurrent render errors that could recover. This used to be the only recoverable error so it didn't need extra context but now we need to distinguish them. When we log these to reportError it's confusing to just see the error because you didn't see anything error on the page. It's also hard to group them together as one.

In this PR, I remove the unnecessary context for hydration mismatches.

For hydration and concurrent errors, I now wrap them in an error that describes that what happened but then use the new cause field to link the original error so we can keep that as the cause. The error that happened was that hydration client rendered or you deopted to sync render, the cause of that error is some other error.

For server errors, we control the Error object so I already had added some context to that error object's message. Since we hide the message in prod, it's nice not to have the raw message in DEV neither. We could potentially split these into two errors for parity though.

We log it as recoverable in a side-channel.
This way they're associated with the error that was thrown. This means
that if there's more than one, there's multiple appendix for each one.
However, since we're now unwinding early this doesn't actually happen
in practice.
This makes it a little easier to group and display as a single error in UIs.

The error is that it caused client rendering - the cause is the other error.

We also exclude the appendix when it's a hydration mismatch since that has its
own description of what happened and no other cause.
@sebmarkbage sebmarkbage requested a review from acdlite April 3, 2024 20:47
@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Apr 3, 2024
@react-sizebot
Copy link

react-sizebot commented Apr 3, 2024

Comparing: 7a2609e...60a2424

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 +0.06% 169.31 kB 169.41 kB +0.12% 52.76 kB 52.83 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js +0.05% 171.13 kB 171.22 kB +0.07% 53.33 kB 53.36 kB
facebook-www/ReactDOM-prod.classic.js +0.23% 589.03 kB 590.40 kB +0.06% 103.68 kB 103.74 kB
facebook-www/ReactDOM-prod.modern.js +0.12% 566.05 kB 566.76 kB +0.09% 99.59 kB 99.68 kB
test_utils/ReactAllWarnings.js Deleted 64.60 kB 0.00 kB Deleted 16.14 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
facebook-www/ReactDOM-prod.classic.js +0.23% 589.03 kB 590.40 kB +0.06% 103.68 kB 103.74 kB
facebook-www/ReactDOMTesting-prod.classic.js +0.23% 603.58 kB 604.95 kB +0.06% 107.35 kB 107.42 kB
facebook-www/ReactDOM-profiling.classic.js +0.22% 618.57 kB 619.94 kB +0.07% 108.02 kB 108.09 kB
test_utils/ReactAllWarnings.js Deleted 64.60 kB 0.00 kB Deleted 16.14 kB 0.00 kB

Generated by 🚫 dangerJS against 60a2424

renderDidError(value);
const wrapperError = new Error(
'There was an error during concurrent rendering but React was able to recover by ' +
'instead synchronously rendering the entire root.',
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Technically I think we could error in the sync pass and then recover in the second sync pass. We could check if this is actually a concurrent render first but we don't really expect this case.

sebmarkbage added a commit that referenced this pull request Apr 4, 2024
Follow up to #28684.

V8 includes the message in the stack and printed errors include just the
stack property which is assumed to contain the message. Without this,
the prefix doesn't get printed in the console.

<img width="578" alt="Screenshot 2024-04-03 at 6 32 04 PM"
src="https://github.com/facebook/react/assets/63648/d98a2db4-6ebc-4805-b669-59f449dfd21f">

A possible alternative would be to use a nested error with a `cause`
like #28736 but that would need some more involved serializing since
this prefix is coming from the server. Perhaps as a separate attribute.
@sebmarkbage sebmarkbage merged commit 6090cab into facebook:main Apr 4, 2024
38 checks passed
github-actions bot pushed a commit that referenced this pull request Apr 4, 2024
Follow up to #28684.

V8 includes the message in the stack and printed errors include just the
stack property which is assumed to contain the message. Without this,
the prefix doesn't get printed in the console.

<img width="578" alt="Screenshot 2024-04-03 at 6 32 04 PM"
src="https://github.com/facebook/react/assets/63648/d98a2db4-6ebc-4805-b669-59f449dfd21f">

A possible alternative would be to use a nested error with a `cause`
like #28736 but that would need some more involved serializing since
this prefix is coming from the server. Perhaps as a separate attribute.

DiffTrain build for [583eb67](583eb67)
github-actions bot pushed a commit that referenced this pull request Apr 4, 2024
…he real Error (#28736)

We basically have four kinds of recoverable errors:

- Hydration mismatches.
- Server errored but client didn't.
- Hydration render errored but client render didn't (in Root or Suspense
boundary).
- Concurrent render errored but synchronous render didn't.

For the first three we log an additional error that the root or Suspense
boundary didn't error. This provides some context about what happened.
However, the problem is that for hydration mismatches that's unnecessary
extra context that is confusing. We also don't log any additional
context for concurrent render errors that could recover. This used to be
the only recoverable error so it didn't need extra context but now we
need to distinguish them. When we log these to `reportError` it's
confusing to just see the error because you didn't see anything error on
the page. It's also hard to group them together as one.

In this PR, I remove the unnecessary context for hydration mismatches.

For hydration and concurrent errors, I now wrap them in an error that
describes that what happened but then use the new `cause` field to link
the original error so we can keep that as the cause. The error that
happened was that hydration client rendered or you deopted to sync
render, the cause of that error is some other error.

For server errors, we control the Error object so I already had added
some context to that error object's message. Since we hide the message
in prod, it's nice not to have the raw message in DEV neither. We could
potentially split these into two errors for parity though.

DiffTrain build for [6090cab](6090cab)
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
Follow up to facebook#28684.

V8 includes the message in the stack and printed errors include just the
stack property which is assumed to contain the message. Without this,
the prefix doesn't get printed in the console.

<img width="578" alt="Screenshot 2024-04-03 at 6 32 04 PM"
src="https://github.com/facebook/react/assets/63648/d98a2db4-6ebc-4805-b669-59f449dfd21f">

A possible alternative would be to use a nested error with a `cause`
like facebook#28736 but that would need some more involved serializing since
this prefix is coming from the server. Perhaps as a separate attribute.
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
…he real Error (facebook#28736)

We basically have four kinds of recoverable errors:

- Hydration mismatches.
- Server errored but client didn't.
- Hydration render errored but client render didn't (in Root or Suspense
boundary).
- Concurrent render errored but synchronous render didn't.

For the first three we log an additional error that the root or Suspense
boundary didn't error. This provides some context about what happened.
However, the problem is that for hydration mismatches that's unnecessary
extra context that is confusing. We also don't log any additional
context for concurrent render errors that could recover. This used to be
the only recoverable error so it didn't need extra context but now we
need to distinguish them. When we log these to `reportError` it's
confusing to just see the error because you didn't see anything error on
the page. It's also hard to group them together as one.

In this PR, I remove the unnecessary context for hydration mismatches.

For hydration and concurrent errors, I now wrap them in an error that
describes that what happened but then use the new `cause` field to link
the original error so we can keep that as the cause. The error that
happened was that hydration client rendered or you deopted to sync
render, the cause of that error is some other error.

For server errors, we control the Error object so I already had added
some context to that error object's message. Since we hide the message
in prod, it's nice not to have the raw message in DEV neither. We could
potentially split these into two errors for parity though.
bigfootjon pushed a commit that referenced this pull request Apr 18, 2024
…he real Error (#28736)

We basically have four kinds of recoverable errors:

- Hydration mismatches.
- Server errored but client didn't.
- Hydration render errored but client render didn't (in Root or Suspense
boundary).
- Concurrent render errored but synchronous render didn't.

For the first three we log an additional error that the root or Suspense
boundary didn't error. This provides some context about what happened.
However, the problem is that for hydration mismatches that's unnecessary
extra context that is confusing. We also don't log any additional
context for concurrent render errors that could recover. This used to be
the only recoverable error so it didn't need extra context but now we
need to distinguish them. When we log these to `reportError` it's
confusing to just see the error because you didn't see anything error on
the page. It's also hard to group them together as one.

In this PR, I remove the unnecessary context for hydration mismatches.

For hydration and concurrent errors, I now wrap them in an error that
describes that what happened but then use the new `cause` field to link
the original error so we can keep that as the cause. The error that
happened was that hydration client rendered or you deopted to sync
render, the cause of that error is some other error.

For server errors, we control the Error object so I already had added
some context to that error object's message. Since we hide the message
in prod, it's nice not to have the raw message in DEV neither. We could
potentially split these into two errors for parity though.

DiffTrain build for commit 6090cab.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants