jestjs/jest · error · AggregateError

Failed to start watch mode.

Error message

Failed to start watch mode.

What it means

WatcherDriver.start() creates one watcher per root using Promise.allSettled; if any root's watcher creation rejects (constructor error or a 240s 'ready' timeout), it closes the watchers that did start and throws an AggregateError with message 'Failed to start watch mode.' whose errors array holds the per-root failures. A second, timeout-based throw with the same message exists at watchers/index.ts:116 for a single root that never emits 'ready' within MAX_WAIT_TIME (240s).

Source

Thrown at packages/jest-haste-map/src/watchers/index.ts:84

      : FSEventsWatcher.isSupported()
        ? (FSEventsWatcher as unknown as WatcherCtor)
        : NodeWatcher;

    const statCache = new Map<string, Stats>();
    const results = await Promise.allSettled(
      this._roots.map(root =>
        this._createWatcher(Backend, root, onChange, statCache),
      ),
    );
    const fulfilled = results
      .filter(r => r.status === 'fulfilled')
      .map(r => (r as PromiseFulfilledResult<IWatcher>).value);
    const rejected = results
      .filter(r => r.status === 'rejected')
      .map(r => (r as PromiseRejectedResult).reason);
    if (rejected.length > 0) {
      await Promise.allSettled(fulfilled.map(w => w.close()));
      throw new AggregateError(rejected, 'Failed to start watch mode.');
    }
    this._watchers = fulfilled;
  }

  async close(): Promise<void> {
    await Promise.all(this._watchers.map(watcher => watcher.close()));
    this._watchers = [];
  }

  private _createWatcher(
    Backend: WatcherCtor,
    root: string,
    onChange: OnChangeCallback,
    statCache: Map<string, Stats>,
  ): Promise<IWatcher> {
    const watcher = new Backend(root, {
      dot: true,
      glob: this._extensions.map(ext => `**/*.${ext}`),

View on GitHub (pinned to f49721c78e)

Solutions

  1. Inspect the AggregateError.errors array (or the inner Error for the timeout variant) for the real per-root cause.
  2. Confirm every entry in roots exists and the Jest process has read permission on it.
  3. On Linux, raise the inotify watcher limit: `echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p`.
  4. For slow initial walks, reduce the watched surface (narrow roots, tighten ignorePattern) or prefer watchman (useWatchman:true) which returns ready faster via clock-based deltas.
  5. If watchman is the culprit, run `watchman shutdown-server` then let Jest restart it, or set watchman:false to use NodeWatcher/FSEventsWatcher.

Example fix

// before — watching a huge root that never becomes ready in 240s
module.exports = { roots: ['<rootDir>'], watchPathIgnorePatterns: [] };

// after — narrow what is watched and let watchman handle deltas
module.exports = {
  roots: ['<rootDir>/src', '<rootDir>/packages'],
  watchPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/build/'],
};
Defensive patterns

Strategy: try-catch

Validate before calling

import {statSync} from 'node:fs';
for (const root of options.roots) {
  statSync(root); // throws if a root is missing/unreadable — fix before watching
}
// On Linux, check inotify budget
import {readFileSync} from 'node:fs';
try {
  const max = readFileSync('/proc/sys/fs/inotify/max_user_watches','utf8').trim();
  if (Number(max) < 524288) console.warn('inotify max_user_watches low:', max);
} catch {} // non-Linux

Try / catch

try {
  await watcherDriver.start(onChange);
} catch (err) {
  if (err instanceof AggregateError && err.message === 'Failed to start watch mode.') {
    for (const e of err.errors) console.error('watcher root failure:', e);
  }
  throw err;
}

Prevention

When it happens

Trigger: Either: (a) one or more new Backend(root, opts) constructors throw (e.g. FSEventsWatcher on non-Darwin, or a permission error opening a root); or (b) a watcher's 'ready' event does not fire within 240s, triggering the setTimeout reject at line 113-117. Both surface as 'Failed to start watch mode.' — the AggregateError variant carries underlying reasons.

Common situations: Watched roots include a path that does not exist or is unreadable; a root is a network mount that never finishes the initial walk; too many inotify watchers on Linux (ENOSPC) causing NodeWatcher to fail; watchman daemon wedged so WatchmanWatcher never becomes ready; very large monorepo exceeding the 240s ready timeout.

Related errors


AI-assisted analysis of jestjs/jest@f49721c78e (2026-08-03). Data as JSON: /data/errors/fca864b6669f07a4.json. Report an issue: GitHub.