or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

error-handling.mdevent-management.mdindex.mdrenderers.mdtask-configuration.mdtask-management.md
tile.json

error-handling.mddocs/

0

# Error Handling

1

2

Comprehensive error handling system with task state management, error collection modes, rollback capabilities, and detailed error reporting for robust task execution.

3

4

## Capabilities

5

6

### ListrError Class

7

8

Main error class that wraps errors with context information and execution path details.

9

10

```typescript { .api }

11

/**

12

* Main error class for Listr task failures

13

* Provides context and path information for debugging

14

* @template Ctx - Context type

15

*/

16

class ListrError<Ctx> extends Error {

17

/** Execution path where error occurred */

18

public path: string[];

19

/** Task context when error occurred */

20

public ctx: Ctx;

21

/** Original error that was wrapped */

22

public error: Error;

23

/** Error type classification */

24

public type: ListrErrorTypes;

25

/** Task that generated the error */

26

public task: Task<Ctx, any, any>;

27

28

/**

29

* Create a new ListrError

30

* @param error - Original error that occurred

31

* @param type - Classification of the error type

32

* @param task - Task instance where error occurred

33

*/

34

constructor(error: Error, type: ListrErrorTypes, task: Task<Ctx, any, any>);

35

36

/**

37

* Get formatted error message with context

38

* @returns Formatted error string

39

*/

40

toString(): string;

41

42

/**

43

* Get error details as JSON

44

* @returns Serializable error information

45

*/

46

toJSON(): {

47

message: string;

48

path: string[];

49

type: ListrErrorTypes;

50

error: {

51

name: string;

52

message: string;

53

stack?: string;

54

};

55

};

56

}

57

```

58

59

### PromptError Class

60

61

Specialized error class for prompt-related failures and user interaction errors.

62

63

```typescript { .api }

64

/**

65

* Error class for prompt-related failures

66

* Used when user prompts fail or are cancelled

67

*/

68

class PromptError extends Error {

69

/** Original prompt error if available */

70

public originalError?: Error;

71

72

/**

73

* Create a new PromptError

74

* @param message - Error message

75

* @param originalError - Optional original error that caused prompt failure

76

*/

77

constructor(message: string, originalError?: Error);

78

}

79

```

80

81

### Error Types Classification

82

83

Enumeration of different error types for categorizing failures and determining handling strategies.

84

85

```typescript { .api }

86

/**

87

* Error type classifications for different failure scenarios

88

*/

89

enum ListrErrorTypes {

90

/** Task will be retried after this error */

91

WILL_RETRY = 'WILL_RETRY',

92

/** Task will be rolled back after this error */

93

WILL_ROLLBACK = 'WILL_ROLLBACK',

94

/** Task failed during rollback operation */

95

HAS_FAILED_TO_ROLLBACK = 'HAS_FAILED_TO_ROLLBACK',

96

/** Task has failed permanently */

97

HAS_FAILED = 'HAS_FAILED',

98

/** Task failed without throwing an error */

99

HAS_FAILED_WITHOUT_ERROR = 'HAS_FAILED_WITHOUT_ERROR'

100

}

101

```

102

103

### Task State Management

104

105

Task state enumeration covering all possible states including error and recovery states.

106

107

```typescript { .api }

108

/**

109

* Complete enumeration of task execution states

110

*/

111

enum ListrTaskState {

112

/** Task is waiting to be executed */

113

WAITING = 'WAITING',

114

/** Task has started execution */

115

STARTED = 'STARTED',

116

/** Task completed successfully */

117

COMPLETED = 'COMPLETED',

118

/** Task failed with an error */

119

FAILED = 'FAILED',

120

/** Task was skipped */

121

SKIPPED = 'SKIPPED',

122

/** Task is performing rollback operations */

123

ROLLING_BACK = 'ROLLING_BACK',

124

/** Task has been rolled back */

125

ROLLED_BACK = 'ROLLED_BACK',

126

/** Task is retrying after a failure */

127

RETRY = 'RETRY',

128

/** Task execution is paused */

129

PAUSED = 'PAUSED',

130

/** Task is waiting for user prompt input */

131

PROMPT = 'PROMPT',

132

/** Task prompt completed successfully */

133

PROMPT_COMPLETED = 'PROMPT_COMPLETED',

134

/** Task prompt failed */

135

PROMPT_FAILED = 'PROMPT_FAILED'

136

}

137

```

138

139

### Error Collection Options

140

141

Configuration options for how errors are collected and reported during task execution.

142

143

```typescript { .api }

144

/**

145

* Error collection configuration in Listr options

146

*/

147

interface ListrBaseClassOptions<Ctx, Renderer, FallbackRenderer> {

148

/**

149

* Error collection mode

150

* - false: Don't collect errors (exit immediately)

151

* - 'minimal': Collect basic error information

152

* - 'full': Collect detailed error information including stack traces

153

*/

154

collectErrors?: false | 'minimal' | 'full';

155

156

/** Exit immediately when any task fails */

157

exitOnError?: boolean;

158

159

/** Exit after rollback operations complete */

160

exitAfterRollback?: boolean;

161

}

162

```

163

164

**Usage Examples:**

165

166

### Basic Error Handling

167

168

```typescript

169

import { Listr, ListrError } from "listr2";

170

171

const tasks = new Listr([

172

{

173

title: "Task that might fail",

174

task: () => {

175

if (Math.random() < 0.5) {

176

throw new Error("Random failure occurred");

177

}

178

return "Success";

179

}

180

},

181

{

182

title: "Task that always runs",

183

task: () => {

184

console.log("This task always executes");

185

}

186

}

187

], {

188

exitOnError: false, // Continue execution even if tasks fail

189

collectErrors: 'full' // Collect detailed error information

190

});

191

192

try {

193

await tasks.run();

194

console.log("All tasks completed");

195

} catch (error) {

196

if (error instanceof ListrError) {

197

console.log("Listr execution failed:");

198

console.log("Path:", error.path);

199

console.log("Type:", error.type);

200

console.log("Original error:", error.error.message);

201

}

202

}

203

```

204

205

### Retry with Error Handling

206

207

```typescript

208

import { Listr, ListrErrorTypes } from "listr2";

209

210

const tasks = new Listr([

211

{

212

title: "Flaky network request",

213

retry: {

214

tries: 3,

215

delay: 1000

216

},

217

task: async (ctx, task) => {

218

const attempt = (ctx.attempt || 0) + 1;

219

ctx.attempt = attempt;

220

221

task.output = `Attempt ${attempt}`;

222

223

// Simulate network request that fails 70% of the time

224

if (Math.random() < 0.7) {

225

throw new Error(`Network request failed (attempt ${attempt})`);

226

}

227

228

task.output = `Success on attempt ${attempt}`;

229

return "Request completed";

230

}

231

}

232

]);

233

234

// Monitor retry attempts

235

tasks.events.on('TASK_ERROR', (event) => {

236

console.log(`Task failed: ${event.error.message}`);

237

if (event.willRetry) {

238

console.log(`Will retry (${event.retryCount + 1} attempts so far)`);

239

} else {

240

console.log("No more retries, task has failed permanently");

241

}

242

});

243

244

try {

245

await tasks.run();

246

} catch (error) {

247

console.log("Final error:", error.message);

248

}

249

```

250

251

### Rollback Functionality

252

253

```typescript

254

import { Listr } from "listr2";

255

256

interface DeployContext {

257

backupId?: string;

258

deploymentId?: string;

259

serverStarted?: boolean;

260

}

261

262

const deployTasks = new Listr<DeployContext>([

263

{

264

title: "Create backup",

265

task: (ctx) => {

266

ctx.backupId = `backup-${Date.now()}`;

267

console.log(`Created backup: ${ctx.backupId}`);

268

}

269

},

270

{

271

title: "Deploy application",

272

task: (ctx) => {

273

ctx.deploymentId = `deploy-${Date.now()}`;

274

275

// Simulate deployment failure

276

if (Math.random() < 0.4) {

277

throw new Error("Deployment failed - server error");

278

}

279

280

console.log(`Deployed: ${ctx.deploymentId}`);

281

},

282

rollback: (ctx, task) => {

283

if (ctx.backupId && ctx.deploymentId) {

284

task.output = `Rolling back deployment ${ctx.deploymentId}...`;

285

console.log(`Restoring from backup: ${ctx.backupId}`);

286

// Simulate rollback operations

287

return new Promise(resolve => setTimeout(resolve, 1000));

288

}

289

}

290

},

291

{

292

title: "Start services",

293

task: (ctx) => {

294

ctx.serverStarted = true;

295

console.log("Services started");

296

},

297

rollback: (ctx, task) => {

298

if (ctx.serverStarted) {

299

task.output = "Stopping services...";

300

console.log("Services stopped");

301

ctx.serverStarted = false;

302

}

303

}

304

}

305

], {

306

exitAfterRollback: true // Exit after rollback operations complete

307

});

308

309

try {

310

await deployTasks.run();

311

console.log("Deployment successful");

312

} catch (error) {

313

console.log("Deployment failed and rolled back:", error.message);

314

}

315

```

316

317

### Error Collection and Reporting

318

319

```typescript

320

import { Listr, ListrError } from "listr2";

321

322

const tasks = new Listr([

323

{

324

title: "Task 1",

325

task: () => {

326

throw new Error("First task error");

327

}

328

},

329

{

330

title: "Task 2",

331

task: () => {

332

throw new Error("Second task error");

333

}

334

},

335

{

336

title: "Task 3",

337

task: () => {

338

console.log("Task 3 executes successfully");

339

}

340

}

341

], {

342

exitOnError: false,

343

collectErrors: 'full'

344

});

345

346

try {

347

await tasks.run();

348

} catch (error) {

349

if (error instanceof ListrError) {

350

console.log("\n=== Error Summary ===");

351

console.log(`Main error: ${error.message}`);

352

console.log(`Error type: ${error.type}`);

353

console.log(`Execution path: ${error.path.join(' → ')}`);

354

355

// Access all collected errors

356

if (tasks.errors.length > 0) {

357

console.log("\n=== All Errors ===");

358

tasks.errors.forEach((err, index) => {

359

console.log(`${index + 1}. ${err.message}`);

360

console.log(` Path: ${err.path.join(' → ')}`);

361

console.log(` Type: ${err.type}`);

362

console.log(` Original: ${err.error.message}`);

363

});

364

}

365

}

366

}

367

```

368

369

### Custom Error Handling per Task

370

371

```typescript

372

import { Listr } from "listr2";

373

374

interface ProcessingContext {

375

strictMode: boolean;

376

errors: string[];

377

}

378

379

const tasks = new Listr<ProcessingContext>([

380

{

381

title: "Critical system task",

382

exitOnError: true, // Always exit if this task fails

383

task: () => {

384

if (Math.random() < 0.2) {

385

throw new Error("Critical system failure");

386

}

387

}

388

},

389

{

390

title: "Optional enhancement",

391

exitOnError: false, // Never exit for this task

392

task: () => {

393

throw new Error("Enhancement failed, but continuing");

394

}

395

},

396

{

397

title: "Context-dependent task",

398

exitOnError: (ctx) => ctx.strictMode, // Exit based on context

399

task: (ctx) => {

400

if (Math.random() < 0.3) {

401

const error = "Context-dependent failure";

402

ctx.errors.push(error);

403

throw new Error(error);

404

}

405

}

406

}

407

]);

408

409

await tasks.run({

410

strictMode: false,

411

errors: []

412

});

413

```

414

415

### Prompt Error Handling

416

417

```typescript

418

import { Listr, PromptError } from "listr2";

419

420

const tasks = new Listr([

421

{

422

title: "Interactive configuration",

423

task: async (ctx, task) => {

424

try {

425

// Simulate prompt interaction

426

const userInput = await new Promise((resolve, reject) => {

427

setTimeout(() => {

428

// Simulate user cancellation or timeout

429

if (Math.random() < 0.3) {

430

reject(new PromptError("User cancelled the prompt"));

431

} else {

432

resolve("user-input-value");

433

}

434

}, 1000);

435

});

436

437

ctx.userConfig = userInput;

438

task.output = "Configuration completed";

439

} catch (error) {

440

if (error instanceof PromptError) {

441

task.output = "Prompt was cancelled, using defaults";

442

ctx.userConfig = "default-value";

443

} else {

444

throw error; // Re-throw non-prompt errors

445

}

446

}

447

}

448

}

449

]);

450

```

451

452

### Advanced Error Analysis

453

454

```typescript

455

import { Listr, ListrError, ListrErrorTypes, ListrTaskState } from "listr2";

456

457

class ErrorAnalyzer {

458

private errors: ListrError<any>[] = [];

459

private retryCount = 0;

460

private rollbackCount = 0;

461

462

analyze(tasks: Listr<any>) {

463

// Listen to various error events

464

tasks.events.on('TASK_ERROR', (event) => {

465

this.retryCount++;

466

console.log(`Retry ${this.retryCount}: ${event.error.message}`);

467

});

468

469

tasks.events.on('TASK_STATE_CHANGED', (event) => {

470

if (event.newState === ListrTaskState.ROLLING_BACK) {

471

this.rollbackCount++;

472

console.log(`Rollback ${this.rollbackCount} initiated for task ${event.taskId}`);

473

}

474

});

475

}

476

477

generateReport(tasksErrors: ListrError<any>[]) {

478

const report = {

479

totalErrors: tasksErrors.length,

480

retryAttempts: this.retryCount,

481

rollbackOperations: this.rollbackCount,

482

errorsByType: {} as Record<ListrErrorTypes, number>,

483

errorsByPath: {} as Record<string, number>

484

};

485

486

tasksErrors.forEach(error => {

487

// Count by error type

488

report.errorsByType[error.type] = (report.errorsByType[error.type] || 0) + 1;

489

490

// Count by execution path

491

const pathKey = error.path.join(' → ');

492

report.errorsByPath[pathKey] = (report.errorsByPath[pathKey] || 0) + 1;

493

});

494

495

return report;

496

}

497

}

498

499

// Usage

500

const analyzer = new ErrorAnalyzer();

501

const tasks = new Listr([

502

// ... tasks with potential failures

503

], {

504

collectErrors: 'full',

505

exitOnError: false

506

});

507

508

analyzer.analyze(tasks);

509

510

try {

511

await tasks.run();

512

} catch (error) {

513

const report = analyzer.generateReport(tasks.errors);

514

console.log("Error Analysis Report:", JSON.stringify(report, null, 2));

515

}

516

```

517

518

## Types

519

520

```typescript { .api }

521

/**

522

* Error context information

523

*/

524

interface ListrErrorContext {

525

/** Task execution path */

526

path: string[];

527

/** Task context at time of error */

528

context: any;

529

/** Timestamp when error occurred */

530

timestamp: Date;

531

/** Whether task will be retried */

532

willRetry: boolean;

533

/** Current retry attempt number */

534

retryAttempt: number;

535

/** Maximum retry attempts allowed */

536

maxRetries: number;

537

}

538

539

/**

540

* Rollback operation result

541

*/

542

interface RollbackResult {

543

/** Whether rollback was successful */

544

success: boolean;

545

/** Error that occurred during rollback, if any */

546

error?: Error;

547

/** Duration of rollback operation */

548

duration: number;

549

/** Additional rollback metadata */

550

metadata?: Record<string, any>;

551

}

552

553

/**

554

* Error collection summary

555

*/

556

interface ErrorSummary {

557

/** Total number of errors */

558

totalErrors: number;

559

/** Errors by type */

560

byType: Record<ListrErrorTypes, ListrError<any>[]>;

561

/** Errors by execution path */

562

byPath: Record<string, ListrError<any>[]>;

563

/** Tasks that were retried */

564

retriedTasks: string[];

565

/** Tasks that were rolled back */

566

rolledBackTasks: string[];

567

}

568

```