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.
// 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
#751With 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.
// 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.
// before
const context = listr.errors[0].ctx
// after — ctx is gone; the failed task is still available
const task = listr.errors[0].taskcloneObject 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
#759The 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
#769Interrupting 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
#769Tasks 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:
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
AbortSignalon the task wrapper astask.signal, so task functions can cooperatively cancel their own asynchronous work likefetch, child processes or timers when the run is interrupted. #769 - Add
listr.cancel()andtask.cancel()to interrupt a run programmatically without an operating-system signal, rolling back or cancelling the in-flight tasks the same wayCtrl+Cdoes. #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-updatetov8andcli-truncatetov6, 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
stderrto keepstdoutclean 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.outputat 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 tostderrwraps to the correct width. #716 task.output = nullno 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.