In Angular, what does the default ErrorHandler do with an uncaught error, and how do you replace it with your own class?
answer
- one handler for the whole app
- default just logs to the console
- handleError(error) is the hook
- provide it in ApplicationConfig
- report, do not recover
basics
~10 sAngular's default ErrorHandler only logs the error with console.error. To replace it, write a class with a handleError(error) method and register { provide: ErrorHandler, useClass: MyHandler } in the application's providers.
solid answer
~40 s`ErrorHandler` from `@angular/core` is the single application-wide hook Angular calls for errors it catches itself — errors thrown while it runs your constructors, lifecycle hooks, template bindings and event listeners. The default implementation does nothing but `console.error('ERROR', error)`, so the app keeps running and nothing is reported anywhere. To change that, you write a class that implements `ErrorHandler` with a `handleError(error)` method and provide it with `{ provide: ErrorHandler, useClass: MyErrorHandler }` in `ApplicationConfig` (or the root NgModule). It is for **reporting** unexpected errors — logging, monitoring — not for recovering: errors you can handle belong in a `try`/`catch` or RxJS `catchError` where they happen.
code
ts · 16 linesimport { ApplicationConfig, ErrorHandler, Injectable, provideBrowserGlobalErrorListeners } from '@angular/core';
@Injectable()
export class ReportingErrorHandler implements ErrorHandler {
handleError(error: unknown): void {
console.error('Unhandled application error', error);
// forward to your monitoring endpoint here, without ever throwing
}
}
export const appConfig: ApplicationConfig = {
providers: [
provideBrowserGlobalErrorListeners(),
{ provide: ErrorHandler, useClass: ReportingErrorHandler },
],
};go deeper
Recall that the default only logs to the console and that you replace it with a class provided under the ErrorHandler token.
Explain which framework-driven code paths feed the handler and why it is for reporting unexpected errors, not recovery.
Harden the handler: normalise non-Error values, never throw from it, keep its dependencies minimal and pair it with global listeners.
Define the error-reporting contract for the whole app: what is handled locally, what is reported globally, and who is alerted.
## What `ErrorHandler` is `ErrorHandler` is a class exported from `@angular/core`. Angular looks up one instance from the application's root injector and calls its `handleError(error)` method whenever the framework itself catches an error that your code did not handle. That happens mainly in code Angular calls on your behalf, where you have nowhere to put a `try` block: - component and directive constructors and lifecycle hooks such as `ngOnInit`; - template expressions evaluated during change detection; - event listeners bound in templates, such as `(click)="save()"`; - a few APIs with an explicit contract to consume async results, such as the `async` pipe; - application bootstrap, including failing startup initializers. ## What the default does The built-in implementation is tiny: `handleError` calls `console.error('ERROR', error)`. It does not rethrow, does not stop the app and does not send anything anywhere. In a browser, a user who hits an error sees a component that stopped updating or a button that did nothing, while the only evidence sits in their own console. ## Replacing it 1. Write a class that `implements ErrorHandler` and defines `handleError(error: unknown): void`. 2. Mark it `@Injectable()` if it needs dependencies, or use `inject()` in field initializers. 3. Provide it at application level with `{ provide: ErrorHandler, useClass: MyErrorHandler }` in `ApplicationConfig.providers` — or in the root NgModule's `providers` in a module-based app. Angular's general error path looks the handler up from the application's root environment injector, so providing a different `ErrorHandler` in a component's or lazy route's `providers` does not give that part of the app its own handler for hook, binding or listener errors. | Aspect | Default `ErrorHandler` | Custom handler | |---|---|---| | Output | `console.error('ERROR', error)` | Whatever you implement: console, monitoring, a UI notice | | Provided by | The browser platform providers | You, with `useClass` in the app providers | | Stops the app | No | No, unless your code does | | Typical addition | — | Context (current URL, build version), deduplication | ## What to put in it - **Keep the console output** in development, or you lose the stack trace you rely on while debugging. - **Normalise the argument.** The parameter is not always an `Error`: it can be a string, an `HttpErrorResponse` or any value someone threw. - **Never throw from it.** A handler that throws loses the original report; wrap reporting code in `try`/`catch`. - **Keep it independent.** Avoid dependencies that might be the thing that failed; Angular resolves your handler lazily for this reason, but the handler's own dependencies still need to be healthy. ## What it is not for The Angular guidance is explicit: errors reported to `ErrorHandler` are **unexpected** errors, possibly leaving the application in a corrupted state. Handling an expected failure — a 404 from an API, a validation error — belongs at the call site with `try`/`catch` or `catchError`, where the code has the context to show a message or retry. The global handler is the last line: record it, alert on it, perhaps show a generic notice. ## Module-based applications In an app bootstrapped with `bootstrapModule`, the same provider goes into the root NgModule's `providers`. The default handler there comes from the browser platform's providers, which `BrowserModule` brings in; the replacement simply overrides that token in the root injector. Because there is one root handler, feature modules should not each provide their own: the last registration in the root injector wins, and lazily loaded modules have their own injectors that framework-level reporting does not consult. ## A short checklist for a custom handler 1. Implements `ErrorHandler`, provided with `useClass` at the root. 2. Logs to the console, at least in development. 3. Normalises the value before using it. 4. Cannot throw. 5. Is paired with `provideBrowserGlobalErrorListeners()` so window-level errors reach it. ## Related pieces - `provideBrowserGlobalErrorListeners()` (v20+, included by the CLI in new apps) forwards the window's `error` and `unhandledrejection` events to the same handler, which matters most in zoneless applications. - In unit tests, `TestBed` rethrows errors that reach the handler by default, so a failing hook fails the test rather than only logging. - An optional `onViewError` method, used by the developer-preview `@boundary` block, receives errors that a template boundary caught.
- Does the default ErrorHandler stop the application after an error?No. It only logs with `console.error` and returns, so the app keeps running. The part of the view that threw may be left half-updated, which is why the guidance treats these errors as unexpected and possibly state-corrupting, and why you report them rather than silently continue.
- Why does TestBed seem to fail tests on errors that only get logged in the browser?`TestBed` rethrows errors that reach the application error handler by default, so a throwing lifecycle hook fails the test instead of printing to the console. A test that deliberately checks resilience can turn that off with `rethrowApplicationErrors: false` in `configureTestingModule`.
The default ErrorHandler is a smoke alarm with no link to the fire station: it makes noise in the room where it went off, and nobody else ever finds out. A custom handler wires it to the station.
saying these in an interview costs you the question
- The default ErrorHandler reloads or stops the application
- ErrorHandler catches every exception thrown anywhere in the app
- A component-level ErrorHandler provider receives that component's hook and listener errors
- handleError always receives an Error instance
- The global handler is the right place to handle expected API failures