CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-jupyter-widgets--base

Foundational classes and utilities for building interactive widgets in Jupyter environments

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

error-handling.mddocs/

Error Handling

Comprehensive error handling components for graceful failure display, debugging support, and user-friendly error presentation in Jupyter widget environments.

Capabilities

Error Widget Creation

Factory functions for creating widget models and views that display error information.

/**
 * Create a widget model class for displaying error information
 * @param error - Error object or value to display
 * @param msg - Optional additional error message
 * @returns Widget model class configured for error display
 */
function createErrorWidgetModel(
  error: unknown,
  msg?: string
): typeof WidgetModel;

/**
 * Create a widget view class for displaying custom error information
 * @param error - Error object or value to display
 * @param msg - Optional additional error message
 * @returns Widget view class configured for error display
 */
function createErrorWidgetView(
  error?: unknown,
  msg?: string
): typeof WidgetView;

Usage Examples:

// Create error model for widget creation failure
const handleWidgetCreationError = (error: Error) => {
  const ErrorModel = createErrorWidgetModel(
    error,
    'Failed to create widget'
  );
  
  const errorWidget = new ErrorModel({}, {
    model_id: generateId(),
    widget_manager: this.widgetManager
  });
  
  return errorWidget;
};

// Create error view for rendering failure
const handleRenderError = (error: Error) => {
  const ErrorView = createErrorWidgetView(
    error,
    'Failed to render widget content'
  );
  
  const errorView = new ErrorView({
    model: this.model
  });
  
  return errorView;
};

// Handle async errors
try {
  const data = await fetchWidgetData();
  this.model.set('data', data);
} catch (error) {
  const ErrorModel = createErrorWidgetModel(error, 'Data loading failed');
  const fallbackWidget = new ErrorModel({}, {
    model_id: 'error-' + Date.now(),
    widget_manager: this.widgetManager
  });
  this.showErrorWidget(fallbackWidget);
}

ErrorWidgetView

Pre-built view component for displaying error information with interactive error details.

/**
 * View component for displaying error information with click-to-expand functionality
 */
class ErrorWidgetView extends DOMWidgetView {
  /**
   * Extract error message and stack trace from the model
   * @returns Object containing optional message and stack trace
   */
  generateErrorMessage(): { msg?: string; stack: string };

  /**
   * Render the error widget with SVG icon and interactive error display
   */
  render(): void;
}

The ErrorWidgetView provides:

  • Visual Error Icon: Displays a broken file SVG icon to indicate an error state
  • Click to Expand: Users can click the icon to view detailed error information
  • Stack Trace Display: Shows full error stack trace and message when expanded
  • Double-click to Collapse: Users can double-click to hide error details
  • Console Logging: Encourages users to check browser console for additional details

Usage Examples:

// Custom error view with additional context
class CustomErrorView extends ErrorWidgetView {
  generateErrorMessage() {
    const baseError = super.generateErrorMessage();
    return {
      ...baseError,
      msg: `Widget Error in ${this.model.get('widget_type')}: ${baseError.msg}`
    };
  }

  render() {
    super.render();
    
    // Add custom styling
    this.el.classList.add('custom-error-widget');
    
    // Add additional error context
    const contextDiv = document.createElement('div');
    contextDiv.innerHTML = `
      <small>Widget ID: ${this.model.model_id}</small><br>
      <small>Time: ${new Date().toLocaleString()}</small>
    `;
    this.el.appendChild(contextDiv);
  }
}

// Use in widget manager
const createErrorView = (model: WidgetModel, error: Error) => {
  const errorView = new CustomErrorView({
    model: model
  });
  
  // Set error data on model
  model.set('error', error);
  model.set('msg', 'Custom widget failed to initialize');
  
  return errorView;
};

Error Handling Patterns

Widget Creation Error Handling

// Robust widget creation with error fallback
const createWidgetWithFallback = async (
  options: IModelOptions,
  widgetManager: IWidgetManager
): Promise<WidgetModel> => {
  try {
    return await widgetManager.new_model(options);
  } catch (error) {
    console.error('Widget creation failed:', error);
    
    // Create error widget as fallback
    const ErrorModel = createErrorWidgetModel(
      error,
      `Failed to create ${options.model_name}`
    );
    
    return new ErrorModel({
      _model_name: 'ErrorWidgetModel',
      _view_name: 'ErrorWidgetView',
      original_model_name: options.model_name,
      original_module: options.model_module,
      error_type: 'creation_failed'
    }, {
      model_id: options.model_id || generateId(),
      widget_manager: widgetManager
    });
  }
};

// Widget manager integration
class RobustWidgetManager implements IWidgetManager {
  async new_model(options: IModelOptions, state?: JSONObject): Promise<WidgetModel> {
    return createWidgetWithFallback(options, this);
  }

  async create_view<VT extends WidgetView>(
    model: WidgetModel,
    options?: unknown
  ): Promise<VT> {
    try {
      return await this.createViewInternal(model, options);
    } catch (error) {
      console.error('View creation failed:', error);
      
      // Return error view instead
      const ErrorView = createErrorWidgetView(
        error,
        `Failed to create view for ${model.get('_model_name')}`
      );
      
      return new ErrorView({
        model: model,
        options: options
      }) as VT;
    }
  }
}

Communication Error Handling

// Robust communication with error recovery
class RobustWidgetModel extends WidgetModel {
  send(content: JSONValue, callbacks?: ICallbacks, buffers?: ArrayBuffer[]): void {
    const enhancedCallbacks: ICallbacks = {
      ...callbacks,
      shell: {
        ...callbacks?.shell,
        error: (msg) => {
          this.handleCommError(msg);
          callbacks?.shell?.error?.(msg);
        }
      },
      iopub: {
        ...callbacks?.iopub,
        error: (msg) => {
          this.handleIOPubError(msg);
          callbacks?.iopub?.error?.(msg);
        }
      }
    };

    try {
      super.send(content, enhancedCallbacks, buffers);
    } catch (error) {
      this.handleSendError(error);
    }
  }

  private handleCommError(msg: any): void {
    console.error('Comm error:', msg.content);
    
    // Create error state
    this.set('_error_state', {
      type: 'comm_error',
      message: msg.content.ename || 'Communication error',
      details: msg.content.evalue || 'Unknown error',
      timestamp: Date.now()
    });
    
    this.save_changes();
  }

  private handleIOPubError(msg: any): void {
    console.error('IOPub error:', msg.content);
    
    // Show error in UI if view exists
    if (this.views) {
      Object.values(this.views).forEach(async (viewPromise) => {
        try {
          const view = await viewPromise;
          this.showErrorInView(view, msg.content);
        } catch (viewError) {
          console.error('Failed to show error in view:', viewError);
        }
      });
    }
  }

  private handleSendError(error: any): void {
    console.error('Send error:', error);
    
    // Attempt to show error through error widget
    const ErrorModel = createErrorWidgetModel(
      error,
      'Communication failed'
    );
    
    // Try to replace current widget with error widget
    this.close();
    // Implementation would replace widget in UI
  }

  private showErrorInView(view: WidgetView, errorContent: any): void {
    // Add error overlay to existing view
    const errorOverlay = document.createElement('div');
    errorOverlay.className = 'widget-error-overlay';
    errorOverlay.innerHTML = `
      <div class="error-message">
        <strong>Widget Error:</strong> ${errorContent.ename || 'Unknown error'}
        <br>
        <small>${errorContent.evalue || 'Check console for details'}</small>
      </div>
    `;
    
    view.el.appendChild(errorOverlay);
  }
}

View Rendering Error Handling

// Safe view rendering with error boundaries
class SafeWidgetView extends DOMWidgetView {
  render(): void {
    try {
      this.renderContent();
    } catch (error) {
      this.renderError(error);
    }
  }

  protected renderContent(): void {
    // Override in subclasses
    throw new Error('renderContent must be implemented');
  }

  protected renderError(error: Error): void {
    console.error('View rendering failed:', error);
    
    // Clear any partial content
    this.el.innerHTML = '';
    
    // Show error widget content
    const errorView = new ErrorWidgetView({
      model: this.model
    });
    
    // Set error data on model temporarily
    const originalError = this.model.get('error');
    const originalMsg = this.model.get('msg');
    
    this.model.set({
      error: error,
      msg: 'Widget rendering failed'
    });
    
    errorView.render();
    this.el.appendChild(errorView.el);
    
    // Restore original values
    this.model.set({
      error: originalError,
      msg: originalMsg
    });
  }

  update(options?: any): void {
    try {
      this.updateContent(options);
    } catch (error) {
      console.error('View update failed:', error);
      this.renderError(error);
    }
  }

  protected updateContent(options?: any): void {
    // Override in subclasses
  }
}

// Usage in custom widgets
class ChartWidgetView extends SafeWidgetView {
  protected renderContent(): void {
    const data = this.model.get('data');
    const chartType = this.model.get('chart_type');
    
    if (!data || !chartType) {
      throw new Error('Missing required data or chart type');
    }
    
    // Render chart content
    this.renderChart(data, chartType);
  }

  protected updateContent(options?: any): void {
    if (this.model.hasChanged('data') || this.model.hasChanged('chart_type')) {
      this.renderContent();
    }
  }
}

Async Error Handling

// Handle async operations in widgets
class AsyncWidgetModel extends WidgetModel {
  private async handleAsyncOperation<T>(
    operation: () => Promise<T>,
    errorContext: string
  ): Promise<T | null> {
    try {
      this.set('loading', true);
      this.save_changes();
      
      const result = await operation();
      
      this.set({
        loading: false,
        error: null
      });
      this.save_changes();
      
      return result;
    } catch (error) {
      console.error(`${errorContext} failed:`, error);
      
      this.set({
        loading: false,
        error: {
          context: errorContext,
          message: error instanceof Error ? error.message : String(error),
          stack: error instanceof Error ? error.stack : undefined,
          timestamp: Date.now()
        }
      });
      this.save_changes();
      
      return null;
    }
  }

  async loadData(): Promise<void> {
    await this.handleAsyncOperation(
      async () => {
        const response = await fetch(this.get('data_url'));
        if (!response.ok) {
          throw new Error(`HTTP ${response.status}: ${response.statusText}`);
        }
        const data = await response.json();
        this.set('data', data);
        return data;
      },
      'Data loading'
    );
  }

  async saveChangesToServer(): Promise<void> {
    await this.handleAsyncOperation(
      async () => {
        const response = await fetch(this.get('save_url'), {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(this.get_state())
        });
        if (!response.ok) {
          throw new Error(`Save failed: ${response.statusText}`);
        }
        return response.json();
      },
      'Saving changes'
    );
  }
}

// View that responds to error states
class AsyncWidgetView extends DOMWidgetView {
  initialize(parameters: WidgetView.IInitializeParameters): void {
    super.initialize(parameters);
    
    this.listenTo(this.model, 'change:error', this.handleErrorChange);
    this.listenTo(this.model, 'change:loading', this.handleLoadingChange);
  }

  private handleErrorChange(): void {
    const error = this.model.get('error');
    
    if (error) {
      this.showError(error);
    } else {
      this.hideError();
    }
  }

  private handleLoadingChange(): void {
    const loading = this.model.get('loading');
    
    if (loading) {
      this.showLoadingIndicator();
    } else {
      this.hideLoadingIndicator();
    }
  }

  private showError(error: any): void {
    // Remove any existing error display
    this.hideError();
    
    const errorDiv = document.createElement('div');
    errorDiv.className = 'widget-error-display';
    errorDiv.innerHTML = `
      <div class="error-header">
        <strong>Error in ${error.context || 'Widget'}:</strong>
      </div>
      <div class="error-message">${error.message}</div>
      <div class="error-time">
        <small>Occurred at: ${new Date(error.timestamp).toLocaleString()}</small>
      </div>
      ${error.stack ? `<details><summary>Stack Trace</summary><pre>${error.stack}</pre></details>` : ''}
    `;
    
    this.el.appendChild(errorDiv);
  }

  private hideError(): void {
    const errorDisplay = this.el.querySelector('.widget-error-display');
    if (errorDisplay) {
      errorDisplay.remove();
    }
  }

  private showLoadingIndicator(): void {
    const loader = document.createElement('div');
    loader.className = 'widget-loading';
    loader.innerHTML = '<div class="loading-spinner"></div><span>Loading...</span>';
    this.el.appendChild(loader);
  }

  private hideLoadingIndicator(): void {
    const loader = this.el.querySelector('.widget-loading');
    if (loader) {
      loader.remove();
    }
  }
}

Error Recovery Strategies

Graceful Degradation

// Widget that degrades gracefully when features are unavailable
class FeatureAwareWidget extends DOMWidgetView {
  render(): void {
    if (this.supportsAdvancedFeatures()) {
      this.renderAdvanced();
    } else if (this.supportsBasicFeatures()) {
      this.renderBasic();
    } else {
      this.renderFallback();
    }
  }

  private supportsAdvancedFeatures(): boolean {
    return 'WebGL2RenderingContext' in window && 
           'OffscreenCanvas' in window;
  }

  private supportsBasicFeatures(): boolean {
    return 'Canvas' in window && 
           'WebGLRenderingContext' in window;
  }

  private renderAdvanced(): void {
    // High-performance WebGL2 rendering
  }

  private renderBasic(): void {
    // Basic WebGL or canvas rendering
  }

  private renderFallback(): void {
    // Simple HTML/CSS fallback
    const message = document.createElement('div');
    message.innerHTML = `
      <div class="feature-warning">
        <p>This browser doesn't support advanced rendering features.</p>
        <p>Showing simplified version.</p>
      </div>
    `;
    this.el.appendChild(message);
  }
}

Error Reporting

// Error reporting and telemetry
class ErrorReporter {
  static reportError(error: Error, context: string, widget?: WidgetModel): void {
    const errorReport = {
      message: error.message,
      stack: error.stack,
      context: context,
      timestamp: Date.now(),
      userAgent: navigator.userAgent,
      widgetInfo: widget ? {
        modelName: widget.get('_model_name'),
        modelModule: widget.get('_model_module'),
        modelId: widget.model_id
      } : undefined
    };

    // Log to console
    console.error('Widget Error Report:', errorReport);

    // Send to error tracking service (if configured)
    this.sendToErrorService(errorReport);
  }

  private static sendToErrorService(report: any): void {
    // Implementation would send to error tracking service
    // like Sentry, LogRocket, etc.
  }
}

// Usage in widgets
class ReportingWidgetModel extends WidgetModel {
  initialize(attributes: any, options: any): void {
    try {
      super.initialize(attributes, options);
    } catch (error) {
      ErrorReporter.reportError(
        error instanceof Error ? error : new Error(String(error)),
        'Widget initialization',
        this
      );
      throw error;
    }
  }
}

docs

communication.md

error-handling.md

index.md

layout-style.md

nativeview.md

registry.md

utilities.md

widget-classes.md

widget-manager.md

tile.json