Repository: facebook/react
Pull request: Include error name in error chunks

Follow-up to https://github.com/facebook/react/pull/32116/

`resolveErrorDev` is used for error values and error chunks. However, https://github.com/facebook/react/pull/32116 only included the name in error values so error chunks were deserialized with an `undefined` name e.g. when we fail to serialize the error message

```
undefined: An error occurred but serializing the error message failed.
```

-- https://github.com/vercel/next.js/actions/runs/12872159135/job/35887113469?pr=75110#step:32:302

Included some types for error info that are shared between Client and Server to avoid these bugs where we only consider error values and not chunks in the future.

Changed files:
filename: packages/react-client/src/ReactFlightClient.js
patch: @@ -16,6 +16,7 @@ import type {
   ReactTimeInfo,
   ReactStackTrace,
   ReactCallSite,
+  ReactErrorInfoDev,
 } from 'shared/ReactTypes';
 import type {LazyComponent} from 'react/src/ReactLazy';
 
@@ -2123,18 +2124,12 @@ function resolveErrorProd(response: Response): Error {
 
 function resolveErrorDev(
   response: Response,
-  errorInfo: {
-    name: string,
-    message: string,
-    stack: ReactStackTrace,
-    env: string,
-    ...
-  },
+  errorInfo: ReactErrorInfoDev,
 ): Error {
-  const name: string = errorInfo.name;
-  const message: string = errorInfo.message;
-  const stack: ReactStackTrace = errorInfo.stack;
-  const env: string = errorInfo.env;
+  const name = errorInfo.name;
+  const message = errorInfo.message;
+  const stack = errorInfo.stack;
+  const env = errorInfo.env;
 
   if (!__DEV__) {
     // These errors should never make it into a build so we don't need to encode them in codes.json

filename: packages/react-client/src/__tests__/ReactFlight-test.js
patch: @@ -1387,6 +1387,7 @@ describe('ReactFlight', () => {
         errors: [
           {
             message: 'This is an error',
+            name: 'Error',
             stack: expect.stringContaining(
               'Error: This is an error\n' +
                 '    at eval (eval at testFunction (inspected-page.html:29:11),%20%3Canonymous%3E:1:35)\n' +

filename: packages/react-server/src/ReactFlightServer.js
patch: @@ -64,6 +64,8 @@ import type {
   ReactTimeInfo,
   ReactStackTrace,
   ReactCallSite,
+  ReactErrorInfo,
+  ReactErrorInfoDev,
 } from 'shared/ReactTypes';
 import type {ReactElement} from 'shared/ReactElementType';
 import type {LazyComponent} from 'react/src/ReactLazy';
@@ -3093,8 +3095,8 @@ function emitPostponeChunk(
 
 function serializeErrorValue(request: Request, error: Error): string {
   if (__DEV__) {
-    let name;
-    let message;
+    let name: string = 'Error';
+    let message: string;
     let stack: ReactStackTrace;
     let env = (0, request.environmentName)();
     try {
@@ -3112,7 +3114,7 @@ function serializeErrorValue(request: Request, error: Error): string {
       message = 'An error occurred but serializing the error message failed.';
       stack = [];
     }
-    const errorInfo = {name, message, stack, env};
+    const errorInfo: ReactErrorInfoDev = {name, message, stack, env};
     const id = outlineModel(request, errorInfo);
     return '$Z' + id.toString(16);
   } else {
@@ -3129,13 +3131,15 @@ function emitErrorChunk(
   digest: string,
   error: mixed,
 ): void {
-  let errorInfo: any;
+  let errorInfo: ReactErrorInfo;
   if (__DEV__) {
-    let message;
+    let name: string = 'Error';
+    let message: string;
     let stack: ReactStackTrace;
     let env = (0, request.environmentName)();
     try {
       if (error instanceof Error) {
+        name = error.name;
         // eslint-disable-next-line react-internal/safe-string-coercion
         message = String(error.message);
         stack = filterStackTrace(request, error, 0);
@@ -3157,7 +3161,7 @@ function emitErrorChunk(
       message = 'An error occurred but serializing the error message failed.';
       stack = [];
     }
-    errorInfo = {digest, message, stack, env};
+    errorInfo = {digest, name, message, stack, env};
   } else {
     errorInfo = {digest};
   }

filename: packages/shared/ReactTypes.js
patch: @@ -203,6 +203,20 @@ export type ReactEnvironmentInfo = {
   +env: string,
 };
 
+export type ReactErrorInfoProd = {
+  +digest: string,
+};
+
+export type ReactErrorInfoDev = {
+  +digest?: string,
+  +name: string,
+  +message: string,
+  +stack: ReactStackTrace,
+  +env: string,
+};
+
+export type ReactErrorInfo = ReactErrorInfoProd | ReactErrorInfoDev;
+
 export type ReactAsyncInfo = {
   +type: string,
   // Stashed Data for the Specific Execution Environment. Not part of the transport protocol