Docs/enterprisev1.0.0-rc.1

Error Boundaries

Overview

ErrorBoundary catches exceptions thrown during component rendering and replaces the failed subtree with a fallback UI, preventing a single error from crashing the entire application. GlobalErrorHandler provides application-wide unhandled error capture for cross-cutting concerns such as analytics and telemetry reporting.

Key Concepts

  • ErrorBoundary wraps an array of child components. If any child's render() throws, ErrorBoundary records the error and renders a fallback instead.
  • DefaultErrorFallback is the built-in fallback component used when no custom fallback is provided; it renders a styled error card with a page-refresh button.
  • GlobalErrorHandler is a singleton that captures both window.error events and unhandled promise rejections, then notifies all registered listeners.
  • ErrorInfo describes where and how an error occurred: the component stack, the boundary that caught it, and an optional error ID.

API Reference

ErrorInfo

interface ErrorInfo {
  componentStack: string;  // stack trace or location string
  errorBoundary?: string;  // name of the boundary that caught the error
  errorId?: string;        // optional unique ID for correlation
}

ErrorBoundary

class ErrorBoundary {
  constructor(children: Component[], fallbackComponent?: Component)

  render(): string              // renders children or fallback
  resetError(): void            // clears error state; next render retries children
  getError(): Error | null
  getErrorInfo(): ErrorInfo | null
}

GlobalErrorHandler

class GlobalErrorHandler {
  static getInstance(): GlobalErrorHandler

  setupGlobalHandlers(): void   // attaches window.onerror and unhandledrejection
  handleError(error: Error, errorInfo?: ErrorInfo): void
  addErrorListener(
    listener: (error: Error, errorInfo?: ErrorInfo) => void
  ): () => void                 // returns unsubscribe function
}

useErrorHandler

function useErrorHandler(): {
  handleError: (error: Error, errorInfo?: ErrorInfo) => void;
  addErrorListener: GlobalErrorHandler['addErrorListener'];
}

Basic Usage

import { ErrorBoundary, DefaultErrorFallback, GlobalErrorHandler } from '@tagna/udiot';

// Wrap components in an error boundary with a custom fallback
const fallback = new DefaultErrorFallback();
const boundary = new ErrorBoundary([new UserProfileComponent(), new ActivityFeedComponent()], fallback);

// Render — catches any thrown errors
const html = boundary.render();

// Reset after user acknowledges an error (e.g., after a retry button click)
boundary.resetError();

Common Patterns

Setting up global error reporting

import { GlobalErrorHandler } from '@tagna/udiot';

const handler = GlobalErrorHandler.getInstance();
handler.setupGlobalHandlers(); // capture window errors and unhandled rejections

// Forward to your telemetry system
handler.addErrorListener((error, errorInfo) => {
  myTelemetryClient.captureException(error, {
    extra: { componentStack: errorInfo?.componentStack },
  });
});

Custom fallback component

import { ErrorBoundary } from '@tagna/udiot';

class ServiceUnavailableFallback {
  render(): string {
    return `
      <div class="error-fallback">
        <h2>Service temporarily unavailable</h2>
        <p>Our team has been notified. Please try again in a few minutes.</p>
      </div>
    `;
  }
}

const boundary = new ErrorBoundary(
  [new PaymentWidget()],
  new ServiceUnavailableFallback()
);

Manual error capture in middleware

Use a public pipeline handler and its error-result contract instead of the internal middleware decorators. For example, call an application-owned reporter inside a handler's try/catch and rethrow if the pipeline should record failure. The caller must still inspect the returned success and error fields.

Browser error boundaries and global browser listeners are not server HTTP error handlers. Use the server adapter for HTTP responses.

Integration Notes

  • ErrorBoundary is designed for component trees that implement a render(): string interface. It is not a decorator — instantiate it programmatically and include it in your render hierarchy.
  • GlobalErrorHandler.setupGlobalHandlers() requires a browser environment (window). Call it once at application startup; calling it multiple times adds duplicate listeners.
  • addErrorListener returns an unsubscribe function. Store and call it to clean up listeners (important in long-running server environments or tests).
  • Integrate with telemetry through an error listener; the internal AILogged decorator is not a public root export.

Pitfalls

  • ErrorBoundary.resetError() only clears the boundary's internal state. If the root cause of the error is not fixed, the next render() call will catch it again immediately.
  • GlobalErrorHandler.handleError() logs to the framework logger and then calls listeners. It does not suppress the error — unhandled promise rejections are still rejected.
  • ErrorBoundary catches errors thrown synchronously from render(). Async errors (e.g., inside setTimeout or fetch) are not caught; use GlobalErrorHandler for those.
  • DefaultErrorFallback renders a window.location.reload() button. In server-side rendering this produces non-functional markup; provide a custom fallback without browser-specific behavior.
From the canonical UDIOT public documentation.Try it in the playground ↗