or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

communication.mderror-handling.mdindex.mdlayout-style.mdnativeview.mdregistry.mdutilities.mdwidget-classes.mdwidget-manager.md
tile.json

error-handling.mddocs/

0

# Error Handling

1

2

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

3

4

## Capabilities

5

6

### Error Widget Creation

7

8

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

9

10

```typescript { .api }

11

/**

12

* Create a widget model class for displaying error information

13

* @param error - Error object or value to display

14

* @param msg - Optional additional error message

15

* @returns Widget model class configured for error display

16

*/

17

function createErrorWidgetModel(

18

error: unknown,

19

msg?: string

20

): typeof WidgetModel;

21

22

/**

23

* Create a widget view class for displaying custom error information

24

* @param error - Error object or value to display

25

* @param msg - Optional additional error message

26

* @returns Widget view class configured for error display

27

*/

28

function createErrorWidgetView(

29

error?: unknown,

30

msg?: string

31

): typeof WidgetView;

32

```

33

34

**Usage Examples:**

35

36

```typescript

37

// Create error model for widget creation failure

38

const handleWidgetCreationError = (error: Error) => {

39

const ErrorModel = createErrorWidgetModel(

40

error,

41

'Failed to create widget'

42

);

43

44

const errorWidget = new ErrorModel({}, {

45

model_id: generateId(),

46

widget_manager: this.widgetManager

47

});

48

49

return errorWidget;

50

};

51

52

// Create error view for rendering failure

53

const handleRenderError = (error: Error) => {

54

const ErrorView = createErrorWidgetView(

55

error,

56

'Failed to render widget content'

57

);

58

59

const errorView = new ErrorView({

60

model: this.model

61

});

62

63

return errorView;

64

};

65

66

// Handle async errors

67

try {

68

const data = await fetchWidgetData();

69

this.model.set('data', data);

70

} catch (error) {

71

const ErrorModel = createErrorWidgetModel(error, 'Data loading failed');

72

const fallbackWidget = new ErrorModel({}, {

73

model_id: 'error-' + Date.now(),

74

widget_manager: this.widgetManager

75

});

76

this.showErrorWidget(fallbackWidget);

77

}

78

```

79

80

### ErrorWidgetView

81

82

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

83

84

```typescript { .api }

85

/**

86

* View component for displaying error information with click-to-expand functionality

87

*/

88

class ErrorWidgetView extends DOMWidgetView {

89

/**

90

* Extract error message and stack trace from the model

91

* @returns Object containing optional message and stack trace

92

*/

93

generateErrorMessage(): { msg?: string; stack: string };

94

95

/**

96

* Render the error widget with SVG icon and interactive error display

97

*/

98

render(): void;

99

}

100

```

101

102

The ErrorWidgetView provides:

103

- **Visual Error Icon**: Displays a broken file SVG icon to indicate an error state

104

- **Click to Expand**: Users can click the icon to view detailed error information

105

- **Stack Trace Display**: Shows full error stack trace and message when expanded

106

- **Double-click to Collapse**: Users can double-click to hide error details

107

- **Console Logging**: Encourages users to check browser console for additional details

108

109

**Usage Examples:**

110

111

```typescript

112

// Custom error view with additional context

113

class CustomErrorView extends ErrorWidgetView {

114

generateErrorMessage() {

115

const baseError = super.generateErrorMessage();

116

return {

117

...baseError,

118

msg: `Widget Error in ${this.model.get('widget_type')}: ${baseError.msg}`

119

};

120

}

121

122

render() {

123

super.render();

124

125

// Add custom styling

126

this.el.classList.add('custom-error-widget');

127

128

// Add additional error context

129

const contextDiv = document.createElement('div');

130

contextDiv.innerHTML = `

131

<small>Widget ID: ${this.model.model_id}</small><br>

132

<small>Time: ${new Date().toLocaleString()}</small>

133

`;

134

this.el.appendChild(contextDiv);

135

}

136

}

137

138

// Use in widget manager

139

const createErrorView = (model: WidgetModel, error: Error) => {

140

const errorView = new CustomErrorView({

141

model: model

142

});

143

144

// Set error data on model

145

model.set('error', error);

146

model.set('msg', 'Custom widget failed to initialize');

147

148

return errorView;

149

};

150

```

151

152

## Error Handling Patterns

153

154

### Widget Creation Error Handling

155

156

```typescript

157

// Robust widget creation with error fallback

158

const createWidgetWithFallback = async (

159

options: IModelOptions,

160

widgetManager: IWidgetManager

161

): Promise<WidgetModel> => {

162

try {

163

return await widgetManager.new_model(options);

164

} catch (error) {

165

console.error('Widget creation failed:', error);

166

167

// Create error widget as fallback

168

const ErrorModel = createErrorWidgetModel(

169

error,

170

`Failed to create ${options.model_name}`

171

);

172

173

return new ErrorModel({

174

_model_name: 'ErrorWidgetModel',

175

_view_name: 'ErrorWidgetView',

176

original_model_name: options.model_name,

177

original_module: options.model_module,

178

error_type: 'creation_failed'

179

}, {

180

model_id: options.model_id || generateId(),

181

widget_manager: widgetManager

182

});

183

}

184

};

185

186

// Widget manager integration

187

class RobustWidgetManager implements IWidgetManager {

188

async new_model(options: IModelOptions, state?: JSONObject): Promise<WidgetModel> {

189

return createWidgetWithFallback(options, this);

190

}

191

192

async create_view<VT extends WidgetView>(

193

model: WidgetModel,

194

options?: unknown

195

): Promise<VT> {

196

try {

197

return await this.createViewInternal(model, options);

198

} catch (error) {

199

console.error('View creation failed:', error);

200

201

// Return error view instead

202

const ErrorView = createErrorWidgetView(

203

error,

204

`Failed to create view for ${model.get('_model_name')}`

205

);

206

207

return new ErrorView({

208

model: model,

209

options: options

210

}) as VT;

211

}

212

}

213

}

214

```

215

216

### Communication Error Handling

217

218

```typescript

219

// Robust communication with error recovery

220

class RobustWidgetModel extends WidgetModel {

221

send(content: JSONValue, callbacks?: ICallbacks, buffers?: ArrayBuffer[]): void {

222

const enhancedCallbacks: ICallbacks = {

223

...callbacks,

224

shell: {

225

...callbacks?.shell,

226

error: (msg) => {

227

this.handleCommError(msg);

228

callbacks?.shell?.error?.(msg);

229

}

230

},

231

iopub: {

232

...callbacks?.iopub,

233

error: (msg) => {

234

this.handleIOPubError(msg);

235

callbacks?.iopub?.error?.(msg);

236

}

237

}

238

};

239

240

try {

241

super.send(content, enhancedCallbacks, buffers);

242

} catch (error) {

243

this.handleSendError(error);

244

}

245

}

246

247

private handleCommError(msg: any): void {

248

console.error('Comm error:', msg.content);

249

250

// Create error state

251

this.set('_error_state', {

252

type: 'comm_error',

253

message: msg.content.ename || 'Communication error',

254

details: msg.content.evalue || 'Unknown error',

255

timestamp: Date.now()

256

});

257

258

this.save_changes();

259

}

260

261

private handleIOPubError(msg: any): void {

262

console.error('IOPub error:', msg.content);

263

264

// Show error in UI if view exists

265

if (this.views) {

266

Object.values(this.views).forEach(async (viewPromise) => {

267

try {

268

const view = await viewPromise;

269

this.showErrorInView(view, msg.content);

270

} catch (viewError) {

271

console.error('Failed to show error in view:', viewError);

272

}

273

});

274

}

275

}

276

277

private handleSendError(error: any): void {

278

console.error('Send error:', error);

279

280

// Attempt to show error through error widget

281

const ErrorModel = createErrorWidgetModel(

282

error,

283

'Communication failed'

284

);

285

286

// Try to replace current widget with error widget

287

this.close();

288

// Implementation would replace widget in UI

289

}

290

291

private showErrorInView(view: WidgetView, errorContent: any): void {

292

// Add error overlay to existing view

293

const errorOverlay = document.createElement('div');

294

errorOverlay.className = 'widget-error-overlay';

295

errorOverlay.innerHTML = `

296

<div class="error-message">

297

<strong>Widget Error:</strong> ${errorContent.ename || 'Unknown error'}

298

<br>

299

<small>${errorContent.evalue || 'Check console for details'}</small>

300

</div>

301

`;

302

303

view.el.appendChild(errorOverlay);

304

}

305

}

306

```

307

308

### View Rendering Error Handling

309

310

```typescript

311

// Safe view rendering with error boundaries

312

class SafeWidgetView extends DOMWidgetView {

313

render(): void {

314

try {

315

this.renderContent();

316

} catch (error) {

317

this.renderError(error);

318

}

319

}

320

321

protected renderContent(): void {

322

// Override in subclasses

323

throw new Error('renderContent must be implemented');

324

}

325

326

protected renderError(error: Error): void {

327

console.error('View rendering failed:', error);

328

329

// Clear any partial content

330

this.el.innerHTML = '';

331

332

// Show error widget content

333

const errorView = new ErrorWidgetView({

334

model: this.model

335

});

336

337

// Set error data on model temporarily

338

const originalError = this.model.get('error');

339

const originalMsg = this.model.get('msg');

340

341

this.model.set({

342

error: error,

343

msg: 'Widget rendering failed'

344

});

345

346

errorView.render();

347

this.el.appendChild(errorView.el);

348

349

// Restore original values

350

this.model.set({

351

error: originalError,

352

msg: originalMsg

353

});

354

}

355

356

update(options?: any): void {

357

try {

358

this.updateContent(options);

359

} catch (error) {

360

console.error('View update failed:', error);

361

this.renderError(error);

362

}

363

}

364

365

protected updateContent(options?: any): void {

366

// Override in subclasses

367

}

368

}

369

370

// Usage in custom widgets

371

class ChartWidgetView extends SafeWidgetView {

372

protected renderContent(): void {

373

const data = this.model.get('data');

374

const chartType = this.model.get('chart_type');

375

376

if (!data || !chartType) {

377

throw new Error('Missing required data or chart type');

378

}

379

380

// Render chart content

381

this.renderChart(data, chartType);

382

}

383

384

protected updateContent(options?: any): void {

385

if (this.model.hasChanged('data') || this.model.hasChanged('chart_type')) {

386

this.renderContent();

387

}

388

}

389

}

390

```

391

392

### Async Error Handling

393

394

```typescript

395

// Handle async operations in widgets

396

class AsyncWidgetModel extends WidgetModel {

397

private async handleAsyncOperation<T>(

398

operation: () => Promise<T>,

399

errorContext: string

400

): Promise<T | null> {

401

try {

402

this.set('loading', true);

403

this.save_changes();

404

405

const result = await operation();

406

407

this.set({

408

loading: false,

409

error: null

410

});

411

this.save_changes();

412

413

return result;

414

} catch (error) {

415

console.error(`${errorContext} failed:`, error);

416

417

this.set({

418

loading: false,

419

error: {

420

context: errorContext,

421

message: error instanceof Error ? error.message : String(error),

422

stack: error instanceof Error ? error.stack : undefined,

423

timestamp: Date.now()

424

}

425

});

426

this.save_changes();

427

428

return null;

429

}

430

}

431

432

async loadData(): Promise<void> {

433

await this.handleAsyncOperation(

434

async () => {

435

const response = await fetch(this.get('data_url'));

436

if (!response.ok) {

437

throw new Error(`HTTP ${response.status}: ${response.statusText}`);

438

}

439

const data = await response.json();

440

this.set('data', data);

441

return data;

442

},

443

'Data loading'

444

);

445

}

446

447

async saveChangesToServer(): Promise<void> {

448

await this.handleAsyncOperation(

449

async () => {

450

const response = await fetch(this.get('save_url'), {

451

method: 'POST',

452

headers: { 'Content-Type': 'application/json' },

453

body: JSON.stringify(this.get_state())

454

});

455

if (!response.ok) {

456

throw new Error(`Save failed: ${response.statusText}`);

457

}

458

return response.json();

459

},

460

'Saving changes'

461

);

462

}

463

}

464

465

// View that responds to error states

466

class AsyncWidgetView extends DOMWidgetView {

467

initialize(parameters: WidgetView.IInitializeParameters): void {

468

super.initialize(parameters);

469

470

this.listenTo(this.model, 'change:error', this.handleErrorChange);

471

this.listenTo(this.model, 'change:loading', this.handleLoadingChange);

472

}

473

474

private handleErrorChange(): void {

475

const error = this.model.get('error');

476

477

if (error) {

478

this.showError(error);

479

} else {

480

this.hideError();

481

}

482

}

483

484

private handleLoadingChange(): void {

485

const loading = this.model.get('loading');

486

487

if (loading) {

488

this.showLoadingIndicator();

489

} else {

490

this.hideLoadingIndicator();

491

}

492

}

493

494

private showError(error: any): void {

495

// Remove any existing error display

496

this.hideError();

497

498

const errorDiv = document.createElement('div');

499

errorDiv.className = 'widget-error-display';

500

errorDiv.innerHTML = `

501

<div class="error-header">

502

<strong>Error in ${error.context || 'Widget'}:</strong>

503

</div>

504

<div class="error-message">${error.message}</div>

505

<div class="error-time">

506

<small>Occurred at: ${new Date(error.timestamp).toLocaleString()}</small>

507

</div>

508

${error.stack ? `<details><summary>Stack Trace</summary><pre>${error.stack}</pre></details>` : ''}

509

`;

510

511

this.el.appendChild(errorDiv);

512

}

513

514

private hideError(): void {

515

const errorDisplay = this.el.querySelector('.widget-error-display');

516

if (errorDisplay) {

517

errorDisplay.remove();

518

}

519

}

520

521

private showLoadingIndicator(): void {

522

const loader = document.createElement('div');

523

loader.className = 'widget-loading';

524

loader.innerHTML = '<div class="loading-spinner"></div><span>Loading...</span>';

525

this.el.appendChild(loader);

526

}

527

528

private hideLoadingIndicator(): void {

529

const loader = this.el.querySelector('.widget-loading');

530

if (loader) {

531

loader.remove();

532

}

533

}

534

}

535

```

536

537

## Error Recovery Strategies

538

539

### Graceful Degradation

540

541

```typescript

542

// Widget that degrades gracefully when features are unavailable

543

class FeatureAwareWidget extends DOMWidgetView {

544

render(): void {

545

if (this.supportsAdvancedFeatures()) {

546

this.renderAdvanced();

547

} else if (this.supportsBasicFeatures()) {

548

this.renderBasic();

549

} else {

550

this.renderFallback();

551

}

552

}

553

554

private supportsAdvancedFeatures(): boolean {

555

return 'WebGL2RenderingContext' in window &&

556

'OffscreenCanvas' in window;

557

}

558

559

private supportsBasicFeatures(): boolean {

560

return 'Canvas' in window &&

561

'WebGLRenderingContext' in window;

562

}

563

564

private renderAdvanced(): void {

565

// High-performance WebGL2 rendering

566

}

567

568

private renderBasic(): void {

569

// Basic WebGL or canvas rendering

570

}

571

572

private renderFallback(): void {

573

// Simple HTML/CSS fallback

574

const message = document.createElement('div');

575

message.innerHTML = `

576

<div class="feature-warning">

577

<p>This browser doesn't support advanced rendering features.</p>

578

<p>Showing simplified version.</p>

579

</div>

580

`;

581

this.el.appendChild(message);

582

}

583

}

584

```

585

586

### Error Reporting

587

588

```typescript

589

// Error reporting and telemetry

590

class ErrorReporter {

591

static reportError(error: Error, context: string, widget?: WidgetModel): void {

592

const errorReport = {

593

message: error.message,

594

stack: error.stack,

595

context: context,

596

timestamp: Date.now(),

597

userAgent: navigator.userAgent,

598

widgetInfo: widget ? {

599

modelName: widget.get('_model_name'),

600

modelModule: widget.get('_model_module'),

601

modelId: widget.model_id

602

} : undefined

603

};

604

605

// Log to console

606

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

607

608

// Send to error tracking service (if configured)

609

this.sendToErrorService(errorReport);

610

}

611

612

private static sendToErrorService(report: any): void {

613

// Implementation would send to error tracking service

614

// like Sentry, LogRocket, etc.

615

}

616

}

617

618

// Usage in widgets

619

class ReportingWidgetModel extends WidgetModel {

620

initialize(attributes: any, options: any): void {

621

try {

622

super.initialize(attributes, options);

623

} catch (error) {

624

ErrorReporter.reportError(

625

error instanceof Error ? error : new Error(String(error)),

626

'Widget initialization',

627

this

628

);

629

throw error;

630

}

631

}

632

}

633

```