From 3261d1439198869a66ffdd05298af56622da0449 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Mon, 21 Apr 2025 09:46:11 -0700 Subject: [PATCH] refactor(core): `NgZone.onError` reports to application error handler (#60946) This commit ensures that errors from the `NgZone.onError` subscription in bootstrap are reported to the internal application error handler. Practically speaking, this should not affect anything today, as the application handler goes directly to the `ErrorHandler` from the root. While TestBed installs an application error handler that rethrows, `TestBed.createComponent` does not create components through this bootstrap function. PR Close #60946 --- packages/core/src/platform/bootstrap.ts | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/packages/core/src/platform/bootstrap.ts b/packages/core/src/platform/bootstrap.ts index a20ede52fb3..3ddcabdf10e 100644 --- a/packages/core/src/platform/bootstrap.ts +++ b/packages/core/src/platform/bootstrap.ts @@ -9,7 +9,7 @@ import {Subscription} from 'rxjs'; import {PROVIDED_NG_ZONE} from '../change_detection/scheduling/ng_zone_scheduling'; import {R3Injector} from '../di/r3_injector'; -import {ErrorHandler} from '../error_handler'; +import {INTERNAL_APPLICATION_ERROR_HANDLER} from '../error_handler'; import {RuntimeError, RuntimeErrorCode} from '../errors'; import {DEFAULT_LOCALE_ID} from '../i18n/localization'; import {LOCALE_ID} from '../i18n/tokens'; @@ -90,17 +90,8 @@ export function bootstrap( } else { config.moduleRef.resolveInjectorInitializers(); } - const exceptionHandler = envInjector.get(ErrorHandler, null); + const exceptionHandler = envInjector.get(INTERNAL_APPLICATION_ERROR_HANDLER); if (typeof ngDevMode === 'undefined' || ngDevMode) { - if (exceptionHandler === null) { - const errorMessage = isApplicationBootstrapConfig(config) - ? 'No `ErrorHandler` found in the Dependency Injection tree.' - : 'No ErrorHandler. Is platform module (BrowserModule) included'; - throw new RuntimeError( - RuntimeErrorCode.MISSING_REQUIRED_INJECTABLE_IN_BOOTSTRAP, - errorMessage, - ); - } if (envInjector.get(PROVIDED_ZONELESS) && envInjector.get(PROVIDED_NG_ZONE)) { throw new RuntimeError( RuntimeErrorCode.PROVIDED_BOTH_ZONE_AND_ZONELESS, @@ -113,9 +104,7 @@ export function bootstrap( let onErrorSubscription: Subscription; ngZone.runOutsideAngular(() => { onErrorSubscription = ngZone.onError.subscribe({ - next: (error: any) => { - exceptionHandler!.handleError(error); - }, + next: exceptionHandler, }); }); @@ -142,7 +131,7 @@ export function bootstrap( }); } - return _callAndReportToErrorHandler(exceptionHandler!, ngZone, () => { + return _callAndReportToErrorHandler(exceptionHandler, ngZone, () => { const initStatus = envInjector.get(ApplicationInitStatus); initStatus.runInitializers(); @@ -203,7 +192,7 @@ function moduleDoBootstrap( } function _callAndReportToErrorHandler( - errorHandler: ErrorHandler, + errorHandler: (e: unknown) => void, ngZone: NgZone, callback: () => any, ): any { @@ -211,7 +200,7 @@ function _callAndReportToErrorHandler( const result = callback(); if (isPromise(result)) { return result.catch((e: any) => { - ngZone.runOutsideAngular(() => errorHandler.handleError(e)); + ngZone.runOutsideAngular(() => errorHandler(e)); // rethrow as the exception handler might not do it throw e; }); @@ -219,7 +208,7 @@ function _callAndReportToErrorHandler( return result; } catch (e) { - ngZone.runOutsideAngular(() => errorHandler.handleError(e)); + ngZone.runOutsideAngular(() => errorHandler(e)); // rethrow as the exception handler might not do it throw e; }