Skip to content
v11.0.0

Breaking Changes

These require action to migrate. Each entry shows what changed and how to update your code.

collectErrors is now a boolean

The Listr option collectErrors no longer accepts 'minimal' or 'full' — it is a boolean. The 'full' mode, which deep-cloned the context into every collected error, has been removed.

typescript
// before
new Listr(tasks, { collectErrors: 'minimal' })

// after — 'minimal' becomes true; 'full' has no replacement
new Listr(tasks, { collectErrors: true })

Listr.errors is null when collection is disabled

#751

With the default collectErrors: false, Listr.errors is now null instead of an empty array. An empty array now unambiguously means "collection is enabled and nothing failed". Because the collection is shared from the root of the run, collectErrors is effectively root-scoped: enabling it only on a subtask while the root leaves it disabled no longer collects those errors.

typescript
// before — always an array
if (listr.errors.length) {
  /* ... */
}

// after — null unless collectErrors is enabled
if (listr.errors?.length) {
  /* ... */
}

ListrError.ctx has been removed

ListrError no longer clones the context into the error instance. Only the error, its type, and the path of the failed task are collected. Read the live task reference if you need more.

typescript
// before
const context = listr.errors[0].ctx

// after — ctx is gone; the failed task is still available
const task = listr.errors[0].task

cloneObject export and rfdc dependency removed

Both existed only for the removed 'full' error collection mode. If you imported cloneObject from listr2, use structuredClone (or your own clone) instead.

eventemitter3 dropped for Node's built-in EventEmitter

#759

The internal event handling now uses Node.js's built-in EventEmitter from node:events. The public API is unchanged; only act if you relied on listr2 pulling in eventemitter3 transitively — install it directly in that case.

Ctrl+C now rolls back before exiting

#769

Interrupting a run with Ctrl+C (SIGINT) now runs the rollback of any in-flight task that defines one and defers the exit(127) until every in-flight rollback has settled, instead of marking the pending tasks as failed and exiting synchronously. If you relied on the previous synchronous fail-and-exit, be aware that rollbacks now run first.

New ListrTaskState.CANCELLED state

#769

Tasks stopped by an interruption that do not roll back — and any tasks that had not started — are now marked CANCELLED (a icon in the built-in renderers) instead of FAILED. A task.isCancelled() helper is available.

If you maintain a custom renderer that switches on the task state, handle the new state:

typescript
switch (task.state) {
  // ...your existing cases

  case ListrTaskState.CANCELLED:
    // render the cancelled task
    break
}

If you assert against the test renderer in your own test suite, the serialized task now carries an isCancelled field and interrupted tasks report the CANCELLED state — regenerate your snapshots (for example jest -u).

Features

  • Expose the run's AbortSignal on the task wrapper as task.signal, so task functions can cooperatively cancel their own asynchronous work like fetch, child processes or timers when the run is interrupted. #769
  • Add listr.cancel() and task.cancel() to interrupt a run programmatically without an operating-system signal, rolling back or cancelling the in-flight tasks the same way Ctrl+C does. #769
  • Reset a task's streamed output by setting task.output = null, which clears what the task already streamed to the output bar and bottom bar in the default renderer. #703
  • Upgrade log-update to v8 and cli-truncate to v6, enabling partial/differential rendering in the default renderer — the task list is redrawn with smaller, targeted terminal writes wrapped in synchronized-output markers (ESC[?2026h/ESC[?2026l) on supporting terminals, reducing flicker. The visible output is unchanged.
  • Document routing all renderer output to stderr to keep stdout clean and pipeable. See Process Output. #716

Bug Fixes

  • Fix a memory leak in the default renderer where a finalized task had its output buffer re-created and its listeners re-attached on every render, which exhausted the heap when another task streamed to task.output at a high rate over a long run. #772
  • Preserve OSC-8 hyperlinks, bells and other formatting in the task output, only stripping the cursor and erase codes that would disrupt the rendering. #768
  • Derive the default renderer's wrap width from the configured output stream instead of process.stdout, so routing the renderer to stderr wraps to the correct width. #716
  • task.output = null no longer renders the literal string "null". #703

Node.js Support

The test and build matrix now covers Node.js 22, 24 and 26. The minimum supported version is unchanged at >=22.13.0.

WARNING

The optional enquirer peer dependency is effectively unmaintained, and a readline change in Node.js 24+ (ERR_USE_AFTER_CLOSE) breaks its teardown in some non-interactive scenarios. Adding Node.js 26 to the test matrix surfaced this. The enquirer prompt adapter is expected to keep working on Node.js 22 and 24, but is no longer guaranteed on Node.js 26 and above — prefer the inquirer adapter on newer Node.js versions.

Documentation

The documentation site now publishes an LLM-friendly form of the docs — llms.txt, llms-full.txt, and per-page Markdown — so AI assistants and agents can consume it directly. See Agents.