Skip to content

Conversation

@sebmarkbage
Copy link
Collaborator

@sebmarkbage sebmarkbage commented Sep 10, 2025

When we report an error we typically log the owner stack of the thing that caught the error. Similarly we restore the console.createTask scope of the catching component when we call reportError or console.error.

We also have a special case if something throws during reconciliation which uses the Server Component task as far as we got before we threw.

https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactChildFiber.js#L1952-L1960

Chrome has since fixed it (on our request) that the Error constructor snapshots the Task at the time the constructor was created and logs that in reportError. This is a good thing since it means we get a coherent stack. Unfortunately, it means that the fake Errors that we create in Flight Client gets a snapshot of the task where they were created so when they're reported in the console they get the root Task instead of the Task of the handler of the error.

Ideally we'd transfer the Task from the server and restore it. However, since we don't instrument the Error object to snapshot the owner and we can't read the native Task (if it's even enabled on the server) we don't actually have a correct snapshot to transfer for a Server Component Error. However, we can use the parent's task for where the error was observed by Flight Server and then encode that as a pseudo owner of the Error.

Then we use this owner as the Task which the Error is created within. Now the client snapshots that Task which is reported by reportError so now we have an async stack for Server Component errors again. (Note that this owner may differ from the one observed by captureOwnerStack which gets the nearest Server Component from where it was caught. We could attach the owner to the Error object and use that owner when calling onCaughtError/onUncaughtError).

Before:

Screenshot 2025-09-10 at 10 57 54 AM

After:

Screenshot 2025-09-10 at 11 06 20 AM

Similarly, there are Errors and warnings created by ChildFiber itself. Those execute in the scope of the general render of the parent Fiber. They used to get the scope of the nearest client component parent (e.g. div in this case) but that's the parent of the Server Component. It would be too expensive to run every level of reconciliation in its own task optimistically, so this does it only when we know that we'll throw or log an error that needs this context. Unfortunately this doesn't cover user space errors (such as if an iterable errors).

Before:

Screenshot 2025-09-10 at 11 31 55 AM

After:

Screenshot 2025-09-10 at 11 50 54 AM Screenshot 2025-09-10 at 11 52 46 AM

@meta-cla meta-cla bot added the CLA Signed label Sep 10, 2025
@github-actions github-actions bot added the React Core Team Opened by a member of the React Core Team label Sep 10, 2025
@react-sizebot
Copy link

Comparing: 886b3d3...b72933b

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.js = 6.68 kB 6.68 kB = 1.83 kB 1.83 kB
oss-stable/react-dom/cjs/react-dom-client.production.js = 530.65 kB 530.67 kB = 93.49 kB 93.49 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.69 kB 6.69 kB = 1.83 kB 1.83 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js = 658.20 kB 658.22 kB = 115.79 kB 115.79 kB
facebook-www/ReactDOM-prod.classic.js = 682.36 kB 682.38 kB = 119.83 kB 119.83 kB
facebook-www/ReactDOM-prod.modern.js = 672.79 kB 672.81 kB = 118.14 kB 118.14 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-client/cjs/react-client-flight.development.js +0.32% 126.24 kB 126.64 kB +0.39% 22.70 kB 22.79 kB
oss-stable/react-client/cjs/react-client-flight.development.js +0.32% 126.26 kB 126.67 kB +0.39% 22.72 kB 22.81 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js +0.31% 130.83 kB 131.24 kB +0.34% 23.83 kB 23.91 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js +0.31% 130.88 kB 131.29 kB +0.33% 23.86 kB 23.94 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js +0.31% 131.42 kB 131.83 kB +0.33% 24.14 kB 24.22 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js +0.31% 131.42 kB 131.83 kB +0.33% 24.14 kB 24.22 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +0.31% 132.79 kB 133.20 kB +0.34% 24.26 kB 24.35 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +0.31% 132.84 kB 133.25 kB +0.34% 24.29 kB 24.37 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +0.30% 183.41 kB 183.97 kB +0.33% 42.01 kB 42.15 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +0.30% 183.43 kB 183.99 kB +0.33% 42.04 kB 42.18 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js +0.30% 134.53 kB 134.93 kB +0.33% 24.64 kB 24.72 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js +0.30% 134.53 kB 134.93 kB +0.33% 24.64 kB 24.72 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js +0.30% 134.65 kB 135.06 kB +0.33% 24.67 kB 24.75 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js +0.30% 134.65 kB 135.06 kB +0.33% 24.67 kB 24.75 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js +0.30% 135.26 kB 135.67 kB +0.33% 24.72 kB 24.80 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js +0.30% 135.31 kB 135.72 kB +0.33% 24.75 kB 24.83 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js +0.30% 135.89 kB 136.29 kB +0.33% 24.81 kB 24.89 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js +0.30% 135.89 kB 136.29 kB +0.33% 24.81 kB 24.89 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js +0.30% 135.99 kB 136.40 kB +0.33% 24.91 kB 24.99 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js +0.30% 136.04 kB 136.45 kB +0.33% 24.93 kB 25.01 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js +0.30% 137.57 kB 137.97 kB +0.38% 24.89 kB 24.98 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js +0.30% 137.57 kB 137.97 kB +0.38% 24.89 kB 24.98 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.development.js +0.29% 139.27 kB 139.68 kB +0.33% 25.17 kB 25.26 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.development.js +0.29% 139.27 kB 139.68 kB +0.33% 25.17 kB 25.26 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js +0.29% 140.57 kB 140.98 kB +0.32% 25.41 kB 25.49 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js +0.29% 140.57 kB 140.98 kB +0.32% 25.41 kB 25.49 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js +0.29% 140.70 kB 141.11 kB +0.32% 25.44 kB 25.52 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js +0.29% 140.70 kB 141.11 kB +0.32% 25.44 kB 25.52 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +0.25% 218.49 kB 219.05 kB +0.32% 48.62 kB 48.77 kB
oss-experimental/react-client/cjs/react-client-flight.development.js +0.24% 167.92 kB 168.33 kB +0.27% 29.37 kB 29.45 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js +0.24% 172.54 kB 172.95 kB +0.26% 30.44 kB 30.52 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js +0.24% 173.11 kB 173.52 kB +0.26% 30.83 kB 30.91 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +0.23% 174.51 kB 174.91 kB +0.26% 30.87 kB 30.95 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js +0.23% 176.21 kB 176.62 kB +0.26% 31.32 kB 31.40 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js +0.23% 176.34 kB 176.75 kB +0.26% 31.35 kB 31.43 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js +0.23% 176.98 kB 177.39 kB +0.23% 31.33 kB 31.40 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js +0.23% 177.54 kB 177.95 kB +0.26% 31.46 kB 31.54 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js +0.23% 177.70 kB 178.11 kB +0.27% 31.50 kB 31.59 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js +0.23% 179.22 kB 179.63 kB +0.26% 31.53 kB 31.61 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.development.js +0.23% 180.92 kB 181.33 kB +0.25% 31.80 kB 31.88 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js +0.22% 182.22 kB 182.63 kB +0.25% 32.04 kB 32.12 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js +0.22% 182.35 kB 182.76 kB +0.25% 32.06 kB 32.14 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js +0.22% 574.82 kB 576.11 kB +0.18% 92.73 kB 92.89 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js +0.22% 574.85 kB 576.14 kB +0.18% 92.73 kB 92.90 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js +0.22% 574.90 kB 576.18 kB +0.18% 92.75 kB 92.92 kB
oss-stable-semver/react-art/cjs/react-art.development.js +0.22% 579.32 kB 580.61 kB +0.19% 92.74 kB 92.92 kB
oss-stable/react-art/cjs/react-art.development.js +0.22% 579.40 kB 580.68 kB +0.19% 92.77 kB 92.94 kB
facebook-www/ReactTestRenderer-dev.modern.js +0.22% 596.51 kB 597.79 kB +0.18% 96.49 kB 96.66 kB
facebook-www/ReactTestRenderer-dev.classic.js +0.22% 596.51 kB 597.80 kB +0.18% 96.49 kB 96.66 kB
oss-experimental/react-server/cjs/react-server-flight.development.js +0.21% 143.21 kB 143.52 kB +0.27% 25.68 kB 25.75 kB
facebook-react-native/react-test-renderer/cjs/ReactTestRenderer-dev.js +0.21% 610.45 kB 611.74 kB +0.17% 97.98 kB 98.14 kB
oss-stable-semver/react-server/cjs/react-server-flight.development.js +0.21% 135.68 kB 135.96 kB +0.19% 24.46 kB 24.51 kB
oss-stable/react-server/cjs/react-server-flight.development.js +0.21% 135.68 kB 135.96 kB +0.19% 24.46 kB 24.51 kB

Generated by 🚫 dangerJS against b72933b

function warnOnSymbolType(returnFiber: Fiber, invalidChild: symbol) {
const debugTask = getCurrentDebugTask();
if (__DEV__ && debugTask !== null) {
debugTask.run(warnOnSymbolTypeImpl.bind(null, returnFiber, invalidChild));
Copy link
Collaborator Author

@sebmarkbage sebmarkbage Sep 10, 2025

Choose a reason for hiding this comment

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

Technically these should probably use this technique instead of creating a fake Fiber that gets logged:

https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactChildFiber.js#L224-L234

So that captureOwnerStack inside console.error can also observe it.

But I'm not bothering with that for now.

It doesn't matter for the throws since Error constructor doesn't observe it anyway.

@sebmarkbage sebmarkbage merged commit 20e5431 into facebook:main Sep 12, 2025
247 checks passed
github-actions bot pushed a commit that referenced this pull request Sep 12, 2025
…s the Error's Task (#34460)

When we report an error we typically log the owner stack of the thing
that caught the error. Similarly we restore the `console.createTask`
scope of the catching component when we call `reportError` or
`console.error`.

We also have a special case if something throws during reconciliation
which uses the Server Component task as far as we got before we threw.

https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactChildFiber.js#L1952-L1960

Chrome has since fixed it (on our request) that the Error constructor
snapshots the Task at the time the constructor was created and logs that
in `reportError`. This is a good thing since it means we get a coherent
stack. Unfortunately, it means that the fake Errors that we create in
Flight Client gets a snapshot of the task where they were created so
when they're reported in the console they get the root Task instead of
the Task of the handler of the error.

Ideally we'd transfer the Task from the server and restore it. However,
since we don't instrument the Error object to snapshot the owner and we
can't read the native Task (if it's even enabled on the server) we don't
actually have a correct snapshot to transfer for a Server Component
Error. However, we can use the parent's task for where the error was
observed by Flight Server and then encode that as a pseudo owner of the
Error.

Then we use this owner as the Task which the Error is created within.
Now the client snapshots that Task which is reported by `reportError` so
now we have an async stack for Server Component errors again. (Note that
this owner may differ from the one observed by `captureOwnerStack` which
gets the nearest Server Component from where it was caught. We could
attach the owner to the Error object and use that owner when calling
`onCaughtError`/`onUncaughtError`).

Before:

<img width="911" height="57" alt="Screenshot 2025-09-10 at 10 57 54 AM"
src="https://github.com/user-attachments/assets/0446ef96-fad9-4e17-8a9a-d89c334233ec"
/>

After:

<img width="910" height="128" alt="Screenshot 2025-09-10 at 11 06 20 AM"
src="https://github.com/user-attachments/assets/b30e5892-cf40-4246-a588-0f309575439b"
/>

Similarly, there are Errors and warnings created by ChildFiber itself.
Those execute in the scope of the general render of the parent Fiber.
They used to get the scope of the nearest client component parent (e.g.
div in this case) but that's the parent of the Server Component. It
would be too expensive to run every level of reconciliation in its own
task optimistically, so this does it only when we know that we'll throw
or log an error that needs this context. Unfortunately this doesn't
cover user space errors (such as if an iterable errors).

Before:

<img width="903" height="298" alt="Screenshot 2025-09-10 at 11 31 55 AM"
src="https://github.com/user-attachments/assets/cffc94da-8c14-4d6e-9a5b-bf0833b8b762"
/>

After:

<img width="1216" height="252" alt="Screenshot 2025-09-10 at 11 50
54 AM"
src="https://github.com/user-attachments/assets/f85f93cf-ab73-4046-af3d-dd93b73b3552"
/>

<img width="412" height="115" alt="Screenshot 2025-09-10 at 11 52 46 AM"
src="https://github.com/user-attachments/assets/a76cef7b-b162-4ecf-9b0a-68bf34afc239"
/>

DiffTrain build for [20e5431](20e5431)
github-actions bot pushed a commit that referenced this pull request Sep 12, 2025
…s the Error's Task (#34460)

When we report an error we typically log the owner stack of the thing
that caught the error. Similarly we restore the `console.createTask`
scope of the catching component when we call `reportError` or
`console.error`.

We also have a special case if something throws during reconciliation
which uses the Server Component task as far as we got before we threw.

https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactChildFiber.js#L1952-L1960

Chrome has since fixed it (on our request) that the Error constructor
snapshots the Task at the time the constructor was created and logs that
in `reportError`. This is a good thing since it means we get a coherent
stack. Unfortunately, it means that the fake Errors that we create in
Flight Client gets a snapshot of the task where they were created so
when they're reported in the console they get the root Task instead of
the Task of the handler of the error.

Ideally we'd transfer the Task from the server and restore it. However,
since we don't instrument the Error object to snapshot the owner and we
can't read the native Task (if it's even enabled on the server) we don't
actually have a correct snapshot to transfer for a Server Component
Error. However, we can use the parent's task for where the error was
observed by Flight Server and then encode that as a pseudo owner of the
Error.

Then we use this owner as the Task which the Error is created within.
Now the client snapshots that Task which is reported by `reportError` so
now we have an async stack for Server Component errors again. (Note that
this owner may differ from the one observed by `captureOwnerStack` which
gets the nearest Server Component from where it was caught. We could
attach the owner to the Error object and use that owner when calling
`onCaughtError`/`onUncaughtError`).

Before:

<img width="911" height="57" alt="Screenshot 2025-09-10 at 10 57 54 AM"
src="https://github.com/user-attachments/assets/0446ef96-fad9-4e17-8a9a-d89c334233ec"
/>

After:

<img width="910" height="128" alt="Screenshot 2025-09-10 at 11 06 20 AM"
src="https://github.com/user-attachments/assets/b30e5892-cf40-4246-a588-0f309575439b"
/>

Similarly, there are Errors and warnings created by ChildFiber itself.
Those execute in the scope of the general render of the parent Fiber.
They used to get the scope of the nearest client component parent (e.g.
div in this case) but that's the parent of the Server Component. It
would be too expensive to run every level of reconciliation in its own
task optimistically, so this does it only when we know that we'll throw
or log an error that needs this context. Unfortunately this doesn't
cover user space errors (such as if an iterable errors).

Before:

<img width="903" height="298" alt="Screenshot 2025-09-10 at 11 31 55 AM"
src="https://github.com/user-attachments/assets/cffc94da-8c14-4d6e-9a5b-bf0833b8b762"
/>

After:

<img width="1216" height="252" alt="Screenshot 2025-09-10 at 11 50
54 AM"
src="https://github.com/user-attachments/assets/f85f93cf-ab73-4046-af3d-dd93b73b3552"
/>

<img width="412" height="115" alt="Screenshot 2025-09-10 at 11 52 46 AM"
src="https://github.com/user-attachments/assets/a76cef7b-b162-4ecf-9b0a-68bf34afc239"
/>

DiffTrain build for [20e5431](20e5431)
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.

4 participants