diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 288d6b7c1d018..4ea66dcddd742 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -2123,8 +2123,15 @@ function resolveErrorProd(response: Response): Error { function resolveErrorDev( response: Response, - errorInfo: {message: string, stack: ReactStackTrace, env: string, ...}, + errorInfo: { + name: string, + message: string, + stack: ReactStackTrace, + env: string, + ... + }, ): Error { + const name: string = errorInfo.name; const message: string = errorInfo.message; const stack: ReactStackTrace = errorInfo.stack; const env: string = errorInfo.env; @@ -2156,6 +2163,7 @@ function resolveErrorDev( error = callStack(); } + (error: any).name = name; (error: any).environmentName = env; return error; } diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index e11c11261406f..44b0c76c3f4fb 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -694,9 +694,17 @@ describe('ReactFlight', () => { }); it('can transport Error objects as values', async () => { + class CustomError extends Error { + constructor(message) { + super(message); + this.name = 'Custom'; + } + } + function ComponentClient({prop}) { return ` is error: ${prop instanceof Error} + name: ${prop.name} message: ${prop.message} stack: ${normalizeCodeLocInfo(prop.stack).split('\n').slice(0, 2).join('\n')} environmentName: ${prop.environmentName} @@ -705,7 +713,7 @@ describe('ReactFlight', () => { const Component = clientReference(ComponentClient); function ServerComponent() { - const error = new Error('hello'); + const error = new CustomError('hello'); return ; } @@ -718,14 +726,16 @@ describe('ReactFlight', () => { if (__DEV__) { expect(ReactNoop).toMatchRenderedOutput(` is error: true + name: Custom message: hello - stack: Error: hello + stack: Custom: hello in ServerComponent (at **) environmentName: Server `); } else { expect(ReactNoop).toMatchRenderedOutput(` is error: true + name: Error message: An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error. stack: Error: An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error. environmentName: undefined diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 67ae695ca0230..cda88fc5c1718 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -3093,10 +3093,12 @@ function emitPostponeChunk( function serializeErrorValue(request: Request, error: Error): string { if (__DEV__) { + let name; let message; let stack: ReactStackTrace; let env = (0, request.environmentName)(); try { + name = error.name; // eslint-disable-next-line react-internal/safe-string-coercion message = String(error.message); stack = filterStackTrace(request, error, 0); @@ -3110,7 +3112,7 @@ function serializeErrorValue(request: Request, error: Error): string { message = 'An error occurred but serializing the error message failed.'; stack = []; } - const errorInfo = {message, stack, env}; + const errorInfo = {name, message, stack, env}; const id = outlineModel(request, errorInfo); return '$Z' + id.toString(16); } else {