Chapter 9.6โ˜• 15 min read

Error Handling in RxJS

Errors in RxJS are fatal โ€” they stop the stream. catchError recovers, retry tries again, throwError re-throws. Build robust error handling with these operators.

01How Errors Flow in RxJS

In RxJS, errors are terminal events โ€” when an error occurs, the Observable stops emitting values.

"Error aaya toh stream band โ€” doosra pani nahi aayega."

import { Observable } from 'rxjs';

new Observable(subscriber => {
  subscriber.next(1);
  subscriber.next(2);
  subscriber.error(new Error('Bhai nahi hua!')); // Error!
  subscriber.next(3); // NEVER REACHED
  subscriber.complete(); // NEVER REACHED
}).subscribe({
  next: v => console.log('Value:', v),
  error: e => console.error('Error:', e.message),
  complete: () => console.log('Done'),
});
// Output:
// Value: 1
// Value: 2
// Error: Bhai nahi hua!

Key points about errors in RxJS:

  • Errors are terminal โ€” stream dies immediately
  • All subsequent .next() and .complete() calls are ignored
  • Error propagates to subscriber's error callback
  • If no error handler โ†’ uncaught error โ†’ app crash
  • Use catchError to recover, retry to try again

Think of it like a pipe โ€” if the pipe breaks, water stops flowing. You need to either fix the pipe (retry) or divert to another pipe (catchError).

02catchError โ€” Handle and Recover

catchError is the RxJS operator that catches an error and returns a fallback Observable โ€” the stream continues with the fallback.

import { catchError, of } from 'rxjs';

this.http.get('/api/biryani').pipe(
  catchError(error => {
    console.error('API failed:', error.message);
    // Return fallback โ€” stream continues!
    return of([]); // of() creates Observable that emits []
  })
).subscribe(data => {
  this.biryanis = data; // [] if API failed
});

"catchError = jugaad โ€” error aaya toh default value de do."

Critical rules for catchError:

// โœ… CORRECT: Returns Observable
catchError(error => of([]))

// โœ… CORRECT: Re-throw with throwError (RxJS 7+)
catchError(error => throwError(() => new CustomError(error)))

// โŒ WRONG: Returns value directly
catchError(error => []) // Error! Must return Observable!

// โŒ WRONG: Doesn't return anything
catchError(error => console.log(error)) // Stream dies! 

catchError MUST return an Observable. Use of() for fallback value, EMPTY to silently complete, throwError to re-throw.

03retry โ€” Try Again

retry(n) resubscribes to the source Observable up to n times if it errors. This is useful for temporary network glitches.

import { retry, catchError, of } from 'rxjs';

this.http.get('/api/biryani').pipe(
  retry(3), // Try 3 more times before giving up
  catchError(error => {
    console.error('All 3 retries failed:', error);
    return of([]);
  })
).subscribe(data => this.biryanis = data);

"retry = phir koshish karo โ€” 3 baar try karo, phir haath pair dhono."

retry behavior:

// Request 1: fails โ†’ retry 1: fails โ†’ retry 2: fails โ†’ retry 3: fails โ†’ catchError โ†’ fallback

// Request 1: fails โ†’ retry 1: succeeds! โ†’ subscriber gets data (no more retries)

// retry(0): No retries โ€” just the original request
// retry(3): Up to 3 retries (4 total attempts including original)
// retry(): INFINITE retries โ€” dangerous!

โš ๏ธ ALWAYS use retry(n) with a number. Never use retry() without arguments โ€” it retries forever (infinite loop if server is permanently down).

For more advanced retry (with delay between attempts), use retryWhen or delay operator.

04throwError โ€” Re-throw Custom Error

throwError creates an Observable that immediately errors. It's used to re-throw errors from catchError with more context.

import { throwError, catchError, of } from 'rxjs';

// Basic โ€” just throw an error
throwError(() => new Error('Nahi hua bhai')).subscribe({
  error: e => console.error(e.message) // "Nahi hua bhai"
});

// With custom error context
catchError(originalError => {
  // Log the original error for debugging
  console.error('Original error:', originalError);

  // Throw a more user-friendly error
  return throwError(() => ({
    message: 'Biryani load nahi hua, thodi der baad try karo',
    code: 'BIRYANI_LOAD_FAILED',
    original: originalError.message,
    timestamp: new Date().toISOString()
  }));
});

"throwError = error ko pack karke bhejo โ€” original + extra info."

throwError in RxJS 7+ (factory function):

// โœ… NEW (RxJS 7+) โ€” factory function REQUIRED
throwError(() => new Error('fail'))

// โŒ OLD (RxJS 6) โ€” deprecated!
throwError(new Error('fail'))

The factory function () => error defers error creation and prevents stack trace pollution. Always use it.

05Error Handling Strategy

A complete error handling strategy for Angular apps has multiple layers.

Layer 1: Service Level โ€” catchError + retry

// biryani.service.ts
getBiryani(id: number): Observable {
  return this.http.get(`/api/biryani/${id}`).pipe(
    retry(2), // Retry network glitches
    catchError(error => {
      if (error.status === 404) {
        return of(null); // Not found โ€” return null gracefully
      }
      // Re-throw serious errors for global handler
      return throwError(() => error);
    })
  );
}

Layer 2: Component Level โ€” Specific Error Handling

this.biryaniService.getBiryani(id).subscribe({
  next: biryani => {
    if (biryani === null) {
      this.showNotFound = true; // Handle 404
    } else {
      this.biryani = biryani;
    }
  },
  error: error => {
    this.showServerError = true; // Handle 500 etc.
  }
});

Layer 3: Global Level โ€” Interceptor

// Interceptor catches 401 โ†’ redirect to login
// Catches 0 โ†’ toast "Network error"
// Catches 500 โ†’ toast "Server problem"

"Service level = local handling, Interceptor = global handling, dono saath mein."

Best practice: Service returns typed fallback or re-throws. Component decides what to show. Interceptor handles global concerns (auth, network).

Key Takeaways

  • โœ… Errors in RxJS are terminal โ€” once error occurs, stream stops, no more values
  • โœ… catchError(error => of(fallback)) โ€” must return Observable, recovers from error with fallback
  • โœ… retry(n) โ€” resubscribes up to n times (for network glitches), ALWAYS set a limit
  • โœ… throwError(() => customError) โ€” factory function REQUIRED in RxJS 7+
  • โœ… Strategy: retry + catchError in service, specific handling in component, global in interceptor
Course Search
Search across all chapters & stages
๐Ÿ“–

Search the course

Type any topic โ€” branching, stash, rebase, hooks โ€” and jump straight to that chapter.

merge branchesgit stashundo commitrebase