diff --git a/src/core/util/error.ts b/src/core/util/error.ts index d97f847fd21..6ef2a6cfc6a 100644 --- a/src/core/util/error.ts +++ b/src/core/util/error.ts @@ -4,6 +4,8 @@ import { inBrowser } from './env' import { isPromise } from 'shared/util' import { pushTarget, popTarget } from '../observer/dep' +let isHandlingError = false + export function handleError(err: Error, vm: any, info: string) { // Deactivate deps tracking while processing error handler to avoid possible infinite rendering. // See: https://github.com/vuejs/vuex/issues/1505 @@ -55,7 +57,12 @@ export function invokeWithErrorHandling( function globalHandleError(err, vm, info) { if (config.errorHandler) { + if (isHandlingError) { + logError(err, vm, info) + return + } try { + isHandlingError = true return config.errorHandler.call(null, err, vm, info) } catch (e: any) { // if the user intentionally throws the original error in the handler, @@ -63,6 +70,8 @@ function globalHandleError(err, vm, info) { if (e !== err) { logError(e, null, 'config.errorHandler') } + } finally { + isHandlingError = false } } logError(err, vm, info) diff --git a/test/unit/features/error-handling.spec.ts b/test/unit/features/error-handling.spec.ts index b90bb947a96..674a7ff5a17 100644 --- a/test/unit/features/error-handling.spec.ts +++ b/test/unit/features/error-handling.spec.ts @@ -216,6 +216,34 @@ describe('Error handling', () => { Vue.config.errorHandler = undefined }) + it('should avoid recursive error handling when errorHandler triggers another error', () => { + const originalHandler = Vue.config.errorHandler + let handlerCalls = 0 + Vue.config.errorHandler = (err, instance) => { + handlerCalls++ + if (handlerCalls === 1 && instance) { + instance.$emit('boom') + } + } + new Vue({ + created() { + this.$on('boom', () => { + throw new Error('error in boom') + }) + }, + render(h) { + throw new Error('error in render') + }, + renderError(h, err) { + return h('div', err.toString()) + } + }).$mount() + expect(handlerCalls).toBe(1) + expect('Error in event handler for "boom"').toHaveBeenWarned() + expect('Error: error in boom').toHaveBeenWarned() + Vue.config.errorHandler = originalHandler + }) + // event handlers that can throw errors or return rejected promise ;[ ['single handler', '
'],