Networks fail, servers return errors, and users need clear feedback when something goes wrong. This lesson covers handling HTTP errors gracefully in Angular applications.
How Angular Represents HTTP Errors
When an HTTP request fails, HttpClient's Observable errors out with an HttpErrorResponse object containing the status code, status text, and, if the server sent one, a parsed error body.
Handling this correctly means catching the error close to where it's meaningful, often in the service layer, and converting it into something the UI can present clearly rather than letting a raw error bubble up unhandled.
getUser(id: string) {
return this.http.get<User>(`/api/users/${id}`).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status === 404) {
return throwError(() => new Error('User not found.'));
}
return throwError(() => new Error('Something went wrong. Please try again.'));
})
);
}
catchError converts a raw HttpErrorResponse into a friendlier Error with a message suited for the UI.
Handling Errors With catchError
http.get(url).pipe(
catchError((error: HttpErrorResponse) => {
// inspect error.status, error.error, error.message
return throwError(() => error); // or return a fallback value with of(...)
})
);
catchError intercepts an error in the Observable pipeline and lets you decide how to respond.
throwError(() => ...) re-throws (optionally transformed) so downstream code still sees a failure.
of(fallbackValue) recovers from an error by emitting a fallback value instead of failing.
error.status is the HTTP status code; error.error is the parsed response body, if any.
Error Handling Cheatsheet
Common error-handling patterns for HTTP requests.
Pattern
Example
Use Case
Re-throw a friendlier error
catchError(() => throwError(() => new Error(...)))
Hide raw HTTP details from the UI
Recover with a fallback
catchError(() => of([]))
Show an empty state instead of crashing
Retry on failure
retry(2)
Automatically retry transient network failures
Retry with backoff
retry({ count: 3, delay: 1000 })
Wait between retry attempts
Global handling
An error-handling interceptor
Consistent handling across all requests
Status-specific handling
if (error.status === 404) { ... }
Different UI for different failure types
Retrying Failed Requests
Transient failures (a flaky network, a brief server hiccup) can sometimes succeed on a second attempt. RxJS's retry operator automatically resubscribes to the source Observable a limited number of times before giving up.
A component that requests data should track an error state alongside loading and data states, so the template can show a clear, actionable message instead of leaving users staring at a blank or broken screen.
export class OrderDetailComponent {
order?: Order;
errorMessage?: string;
ngOnInit() {
this.orderService.getOrder(this.orderId).subscribe({
next: order => (this.order = order),
error: () => (this.errorMessage = 'Unable to load this order right now.'),
});
}
}
A Centralized Error Handler
For consistent, app-wide behavior (like logging every unhandled error to a monitoring service), Angular lets you provide a custom ErrorHandler, a single place errors ultimately funnel through if not caught earlier.
@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
handleError(error: unknown) {
console.error('Unhandled error:', error);
// send to a monitoring service, e.g. Sentry
}
}
// app.config.ts
providers: [{ provide: ErrorHandler, useClass: GlobalErrorHandler }]
Common Mistakes
Letting raw HttpErrorResponse objects (or their technical messages) reach the UI directly, confusing non-technical users.
Retrying non-idempotent requests (like a payment POST) blindly, which can cause duplicate side effects.
Forgetting to reset a previous error state before a new request starts, leaving a stale error message visible.
Handling every error case identically instead of distinguishing network failures, validation errors, and server errors.
Key Takeaways
Failed HTTP requests error out with an HttpErrorResponse containing status and response body details.
catchError lets you translate raw errors into friendlier messages or recover with a fallback value.
retry/retry({ count, delay }) can automatically retry transient failures.
A custom ErrorHandler provides a centralized, app-wide fallback for unhandled errors.
Pro Tip
Only apply automatic retries to safe, idempotent requests like GET requests; retrying a POST/PUT/DELETE automatically can silently duplicate an action if the first attempt actually succeeded but the response was lost.
You now understand HTTP error handling strategies. Next, learn about Loading States to give users clear feedback while requests are in flight.