Skip to content

Commit ee0248a

Browse files
committed
fix(runtime-core): errors during component patch should be caught by error handlers
1 parent 3d34f40 commit ee0248a

File tree

4 files changed

+43
-10
lines changed

4 files changed

+43
-10
lines changed

packages/runtime-core/__tests__/errorHandling.spec.ts

+30
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
type VNode,
23
createApp,
34
defineComponent,
45
h,
@@ -11,6 +12,7 @@ import {
1112
watch,
1213
watchEffect,
1314
} from '@vue/runtime-test'
15+
import { ErrorCodes, ErrorTypeStrings } from '../src/errorHandling'
1416

1517
describe('error handling', () => {
1618
test('propagation', () => {
@@ -609,5 +611,33 @@ describe('error handling', () => {
609611
expect(handler).toHaveBeenCalledTimes(1)
610612
})
611613

614+
test('errors in scheduler job with owner instance should be caught', async () => {
615+
let vnode: VNode
616+
const x = ref(0)
617+
const app = createApp({
618+
render() {
619+
return (vnode = vnode || h('div', x.value))
620+
},
621+
})
622+
623+
app.config.errorHandler = vi.fn()
624+
app.mount(nodeOps.createElement('div'))
625+
626+
const error = new Error('error')
627+
Object.defineProperty(vnode!, 'el', {
628+
get() {
629+
throw error
630+
},
631+
})
632+
633+
x.value++
634+
await nextTick()
635+
expect(app.config.errorHandler).toHaveBeenCalledWith(
636+
error,
637+
{},
638+
ErrorTypeStrings[ErrorCodes.COMPONENT_UPDATE],
639+
)
640+
})
641+
612642
// native event handler handling should be tested in respective renderers
613643
})

packages/runtime-core/src/errorHandling.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export enum ErrorCodes {
2323
FUNCTION_REF,
2424
ASYNC_COMPONENT_LOADER,
2525
SCHEDULER,
26+
COMPONENT_UPDATE,
2627
}
2728

2829
export const ErrorTypeStrings: Record<LifecycleHooks | ErrorCodes, string> = {
@@ -54,16 +55,15 @@ export const ErrorTypeStrings: Record<LifecycleHooks | ErrorCodes, string> = {
5455
[ErrorCodes.APP_WARN_HANDLER]: 'app warnHandler',
5556
[ErrorCodes.FUNCTION_REF]: 'ref function',
5657
[ErrorCodes.ASYNC_COMPONENT_LOADER]: 'async component loader',
57-
[ErrorCodes.SCHEDULER]:
58-
'scheduler flush. This is likely a Vue internals bug. ' +
59-
'Please open an issue at https://github.com/vuejs/core .',
58+
[ErrorCodes.SCHEDULER]: 'scheduler flush',
59+
[ErrorCodes.COMPONENT_UPDATE]: 'component update',
6060
}
6161

6262
export type ErrorTypes = LifecycleHooks | ErrorCodes
6363

6464
export function callWithErrorHandling(
6565
fn: Function,
66-
instance: ComponentInternalInstance | null,
66+
instance: ComponentInternalInstance | null | undefined,
6767
type: ErrorTypes,
6868
args?: unknown[],
6969
) {
@@ -105,7 +105,7 @@ export function callWithAsyncErrorHandling(
105105

106106
export function handleError(
107107
err: unknown,
108-
instance: ComponentInternalInstance | null,
108+
instance: ComponentInternalInstance | null | undefined,
109109
type: ErrorTypes,
110110
throwInDev = true,
111111
) {

packages/runtime-core/src/renderer.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1587,6 +1587,7 @@ function baseCreateRenderer(
15871587
effect.run()
15881588
}
15891589
})
1590+
update.i = instance
15901591
update.id = instance.uid
15911592
// allowRecurse
15921593
// #1801, #2043 component render effects should allow recursive updates
@@ -1599,7 +1600,6 @@ function baseCreateRenderer(
15991600
effect.onTrigger = instance.rtg
16001601
? e => invokeArrayFns(instance.rtg!, e)
16011602
: void 0
1602-
update.ownerInstance = instance
16031603
}
16041604

16051605
update()

packages/runtime-core/src/scheduler.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,8 @@ export interface SchedulerJob extends Function {
2626
/**
2727
* Attached by renderer.ts when setting up a component's render effect
2828
* Used to obtain component information when reporting max recursive updates.
29-
* dev only.
3029
*/
31-
ownerInstance?: ComponentInternalInstance
30+
i?: ComponentInternalInstance
3231
}
3332

3433
export type SchedulerJobs = SchedulerJob | SchedulerJob[]
@@ -240,7 +239,11 @@ function flushJobs(seen?: CountMap) {
240239
if (__DEV__ && check(job)) {
241240
continue
242241
}
243-
callWithErrorHandling(job, null, ErrorCodes.SCHEDULER)
242+
callWithErrorHandling(
243+
job,
244+
job.i,
245+
job.i ? ErrorCodes.COMPONENT_UPDATE : ErrorCodes.SCHEDULER,
246+
)
244247
}
245248
}
246249
} finally {
@@ -265,7 +268,7 @@ function checkRecursiveUpdates(seen: CountMap, fn: SchedulerJob) {
265268
} else {
266269
const count = seen.get(fn)!
267270
if (count > RECURSION_LIMIT) {
268-
const instance = fn.ownerInstance
271+
const instance = fn.i
269272
const componentName = instance && getComponentName(instance.type)
270273
handleError(
271274
`Maximum recursive updates exceeded${

0 commit comments

Comments
 (0)