Skip to content

Commit 855670e

Browse files
authored
[dev-overlay] Remove "Unhandled Runtime Error" label (#77484)
1 parent ef1bc0c commit 855670e

File tree

28 files changed

+191
-180
lines changed

28 files changed

+191
-180
lines changed

packages/next/src/client/components/errors/console-error.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@ const consoleTypeSym = Symbol.for('next.console.error.type')
44

55
// Represent non Error shape unhandled promise rejections or console.error errors.
66
// Those errors will be captured and displayed in Error Overlay.
7-
type UnhandledError = Error & {
8-
[digestSym]: 'NEXT_UNHANDLED_ERROR'
7+
export type ConsoleError = Error & {
8+
[digestSym]: 'NEXT_CONSOLE_ERROR'
99
[consoleTypeSym]: 'string' | 'error'
1010
environmentName: string
1111
}
1212

13-
export function createUnhandledError(
13+
export function createConsoleError(
1414
message: string | Error,
1515
environmentName?: string | null
16-
): UnhandledError {
16+
): ConsoleError {
1717
const error = (
1818
typeof message === 'string' ? new Error(message) : message
19-
) as UnhandledError
20-
error[digestSym] = 'NEXT_UNHANDLED_ERROR'
19+
) as ConsoleError
20+
error[digestSym] = 'NEXT_CONSOLE_ERROR'
2121
error[consoleTypeSym] = typeof message === 'string' ? 'string' : 'error'
2222

2323
if (environmentName && !error.environmentName) {
@@ -27,12 +27,10 @@ export function createUnhandledError(
2727
return error
2828
}
2929

30-
export const isUnhandledConsoleOrRejection = (
31-
error: any
32-
): error is UnhandledError => {
33-
return error && error[digestSym] === 'NEXT_UNHANDLED_ERROR'
30+
export const isConsoleError = (error: any): error is ConsoleError => {
31+
return error && error[digestSym] === 'NEXT_CONSOLE_ERROR'
3432
}
3533

36-
export const getUnhandledErrorType = (error: UnhandledError) => {
34+
export const getConsoleErrorType = (error: ConsoleError) => {
3735
return error[consoleTypeSym]
3836
}

packages/next/src/client/components/errors/use-error-handler.ts

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { isNextRouterError } from '../is-next-router-error'
44
import { storeHydrationErrorStateFromConsoleArgs } from './hydration-error-info'
55
import { formatConsoleArgs, parseConsoleArgs } from '../../lib/console'
66
import isError from '../../../lib/is-error'
7-
import { createUnhandledError } from './console-error'
7+
import { createConsoleError } from './console-error'
88
import { enqueueConsecutiveDedupedError } from './enqueue-client-error'
99
import { getReactStitchedError } from '../errors/stitched-error'
1010

@@ -18,21 +18,19 @@ const errorHandlers: Array<ErrorHandler> = []
1818
const rejectionQueue: Array<Error> = []
1919
const rejectionHandlers: Array<ErrorHandler> = []
2020

21-
export function handleClientError(
21+
export function handleConsoleError(
2222
originError: unknown,
23-
consoleErrorArgs: any[],
24-
capturedFromConsole: boolean = false
23+
consoleErrorArgs: any[]
2524
) {
2625
let error: Error
27-
if (!originError || !isError(originError)) {
28-
// If it's not an error, format the args into an error
29-
const formattedErrorMessage = formatConsoleArgs(consoleErrorArgs)
30-
const { environmentName } = parseConsoleArgs(consoleErrorArgs)
31-
error = createUnhandledError(formattedErrorMessage, environmentName)
26+
const { environmentName } = parseConsoleArgs(consoleErrorArgs)
27+
if (isError(originError)) {
28+
error = createConsoleError(originError, environmentName)
3229
} else {
33-
error = capturedFromConsole
34-
? createUnhandledError(originError)
35-
: originError
30+
error = createConsoleError(
31+
formatConsoleArgs(consoleErrorArgs),
32+
environmentName
33+
)
3634
}
3735
error = getReactStitchedError(error)
3836

@@ -49,6 +47,29 @@ export function handleClientError(
4947
}
5048
}
5149

50+
export function handleClientError(originError: unknown) {
51+
let error: Error
52+
if (isError(originError)) {
53+
error = originError
54+
} else {
55+
// If it's not an error, format the args into an error
56+
const formattedErrorMessage = originError + ''
57+
error = new Error(formattedErrorMessage)
58+
}
59+
error = getReactStitchedError(error)
60+
61+
attachHydrationErrorState(error)
62+
63+
enqueueConsecutiveDedupedError(errorQueue, error)
64+
for (const handler of errorHandlers) {
65+
// Delayed the error being passed to React Dev Overlay,
66+
// avoid the state being synchronously updated in the component.
67+
queueMicroTask(() => {
68+
handler(error)
69+
})
70+
}
71+
}
72+
5273
export function useErrorHandler(
5374
handleOnUnhandledError: ErrorHandler,
5475
handleOnUnhandledRejection: ErrorHandler
@@ -85,7 +106,7 @@ function onUnhandledError(event: WindowEventMap['error']): void | boolean {
85106
// When there's an error property present, we log the error to error overlay.
86107
// Otherwise we don't do anything as it's not logging in the console either.
87108
if (event.error) {
88-
handleClientError(event.error, [])
109+
handleClientError(event.error)
89110
}
90111
}
91112

@@ -98,7 +119,7 @@ function onUnhandledRejection(ev: WindowEventMap['unhandledrejection']): void {
98119

99120
let error = reason
100121
if (error && !isError(error)) {
101-
error = createUnhandledError(error + '')
122+
error = new Error(error + '')
102123
}
103124

104125
rejectionQueue.push(error)

packages/next/src/client/components/globals/intercept-console-error.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import isError from '../../../lib/is-error'
22
import { isNextRouterError } from '../is-next-router-error'
3-
import { handleClientError } from '../errors/use-error-handler'
3+
import { handleConsoleError } from '../errors/use-error-handler'
44
import { parseConsoleArgs } from '../../lib/console'
55

66
export const originConsoleError = globalThis.console.error
@@ -29,12 +29,11 @@ export function patchConsoleError() {
2929

3030
if (!isNextRouterError(maybeError)) {
3131
if (process.env.NODE_ENV !== 'production') {
32-
handleClientError(
32+
handleConsoleError(
3333
// replayed errors have their own complex format string that should be used,
3434
// but if we pass the error directly, `handleClientError` will ignore it
3535
maybeError,
36-
args,
37-
true
36+
args
3837
)
3938
}
4039

packages/next/src/client/components/react-dev-overlay/app/app-dev-overlay.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function ReplaySsrOnlyErrors({
5555
// TODO(veil): Produces wrong Owner Stack
5656
// TODO(veil): Mark as recoverable error
5757
// TODO(veil): console.error
58-
handleClientError(ssrError, [])
58+
handleClientError(ssrError)
5959

6060
// If it's missing root tags, we can't recover, make it blocking.
6161
if (ssrError.digest === MISSING_ROOT_TAGS_ERROR) {

packages/next/src/client/components/react-dev-overlay/ui/components/errors/error-overlay-layout/error-overlay-layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import { EnvironmentNameLabel } from '../environment-name-label/environment-name
3737
import { useFocusTrap } from '../dev-tools-indicator/utils'
3838
import { Fader } from '../../fader'
3939

40-
interface ErrorOverlayLayoutProps extends ErrorBaseProps {
40+
export interface ErrorOverlayLayoutProps extends ErrorBaseProps {
4141
errorMessage: ErrorMessageType
4242
errorType: ErrorType
4343
children?: React.ReactNode

packages/next/src/client/components/react-dev-overlay/ui/components/errors/error-type-label/error-type-label.stories.tsx

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,6 @@ export const ConsoleError: Story = {
3131
},
3232
}
3333

34-
export const UnhandledRuntimeError: Story = {
35-
args: {
36-
errorType: 'Unhandled Runtime Error',
37-
},
38-
}
39-
4034
export const MissingRequiredHTMLTag: Story = {
4135
args: {
4236
errorType: 'Missing Required HTML Tag',

packages/next/src/client/components/react-dev-overlay/ui/components/errors/error-type-label/error-type-label.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ export type ErrorType =
22
| 'Build Error'
33
| 'Runtime Error'
44
| 'Console Error'
5-
| 'Unhandled Runtime Error'
65
| 'Missing Required HTML Tag'
76

87
type ErrorTypeLabelProps = {

packages/next/src/client/components/react-dev-overlay/ui/container/errors.tsx

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@ import {
1010
getHydrationWarningType,
1111
} from '../../../errors/hydration-error-info'
1212
import {
13-
getUnhandledErrorType,
14-
isUnhandledConsoleOrRejection,
13+
isConsoleError,
14+
getConsoleErrorType,
1515
} from '../../../errors/console-error'
1616
import { extractNextErrorCode } from '../../../../../lib/error-telemetry-utils'
17-
import { ErrorOverlayLayout } from '../components/errors/error-overlay-layout/error-overlay-layout'
17+
import {
18+
ErrorOverlayLayout,
19+
type ErrorOverlayLayoutProps,
20+
} from '../components/errors/error-overlay-layout/error-overlay-layout'
1821
import { NEXTJS_HYDRATION_ERROR_LINK } from '../../../is-hydration-error'
1922
import type { ReadyRuntimeError } from '../../utils/get-error-by-type'
2023
import type { ErrorBaseProps } from '../components/errors/error-overlay/error-overlay'
@@ -38,20 +41,16 @@ function ErrorDescription({
3841
error: Error
3942
hydrationWarning: string | null
4043
}) {
41-
const isUnhandledOrReplayError = isUnhandledConsoleOrRejection(error)
42-
const unhandledErrorType = isUnhandledOrReplayError
43-
? getUnhandledErrorType(error)
44+
const unhandledErrorType = isConsoleError(error)
45+
? getConsoleErrorType(error)
4446
: null
4547
const isConsoleErrorStringMessage = unhandledErrorType === 'string'
4648
// If the error is:
4749
// - hydration warning
4850
// - captured console error or unhandled rejection
4951
// skip displaying the error name
5052
const title =
51-
(isUnhandledOrReplayError && isConsoleErrorStringMessage) ||
52-
hydrationWarning
53-
? ''
54-
: error.name + ': '
53+
isConsoleErrorStringMessage || hydrationWarning ? '' : error.name + ': '
5554

5655
const environmentName =
5756
'environmentName' in error ? error.environmentName : ''
@@ -75,6 +74,13 @@ function ErrorDescription({
7574
)
7675
}
7776

77+
function getErrorType(error: Error): ErrorOverlayLayoutProps['errorType'] {
78+
if (isConsoleError(error)) {
79+
return 'Console Error'
80+
}
81+
return 'Runtime Error'
82+
}
83+
7884
export function Errors({
7985
runtimeErrors,
8086
debugInfo,
@@ -119,7 +125,7 @@ export function Errors({
119125
const isServerError = ['server', 'edge-server'].includes(
120126
getErrorSource(error) || ''
121127
)
122-
const isUnhandledError = isUnhandledConsoleOrRejection(error)
128+
const errorType = getErrorType(error)
123129
const errorDetails: HydrationErrorState = (error as any).details || {}
124130
const notes = errorDetails.notes || ''
125131
const [warningTemplate, serverContent, clientContent] =
@@ -145,13 +151,7 @@ export function Errors({
145151
return (
146152
<ErrorOverlayLayout
147153
errorCode={errorCode}
148-
errorType={
149-
isServerError
150-
? 'Runtime Error'
151-
: isUnhandledError
152-
? 'Console Error'
153-
: 'Unhandled Runtime Error'
154-
}
154+
errorType={errorType}
155155
errorMessage={
156156
<ErrorDescription error={error} hydrationWarning={hydrationWarning} />
157157
}

packages/next/src/client/react-client-callbacks/error-boundary-callbacks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export function onCaughtError(
7979
// Log and report the error with location but without modifying the error stack
8080
originConsoleError('%o\n\n%s', err, errorLocation)
8181

82-
handleClientError(stitchedError, [])
82+
handleClientError(stitchedError)
8383
} else {
8484
originConsoleError(err)
8585
}

0 commit comments

Comments
 (0)