jestjs/jest · error · Error

Crawler retry failed: Original error: ${retryError.message

Error message

Crawler retry failed:
  Original error: ${retryError.message}
  Retry error: ${error.message}

What it means

jest-haste-map crawls the filesystem with either watchman (preferred) or a node-based crawler. When the watchman crawl fails it retries once with nodeCrawl; this error is thrown only when that fallback ALSO fails, so the message concatenates the original watchman error and the retry error. It tells you both layers of crawling are broken and the actionable detail is in the second (retry) error.

Source

Thrown at packages/jest-haste-map/src/crawlers/index.ts:30

export async function crawl(
  crawlerOptions: CrawlerOptions,
  useWatchman: boolean,
  console: Console,
): ReturnType<typeof nodeCrawl> {
  const crawlFn = useWatchman ? watchmanCrawl : nodeCrawl;

  const retry = (retryError: Error) => {
    if (crawlFn === watchmanCrawl) {
      console.warn(
        'jest-haste-map: Watchman crawl failed. Retrying once with node ' +
          'crawler.\n' +
          "  Usually this happens when watchman isn't running. Create an " +
          "empty `.watchmanconfig` file in your project's root folder or " +
          'initialize a git or hg repository in your project.\n' +
          `  ${retryError}`,
      );
      return nodeCrawl(crawlerOptions).catch(error => {
        throw new Error(
          'Crawler retry failed:\n' +
            `  Original error: ${retryError.message}\n` +
            `  Retry error: ${error.message}\n`,
        );
      });
    }

    throw retryError;
  };

  try {
    return await crawlFn(crawlerOptions);
  } catch (error: any) {
    return retry(error);
  }
}

View on GitHub (pinned to f49721c78e)

Solutions

  1. Read both messages: the 'Retry error' (second line) is the actionable one — fix that root cause first.
  2. Verify all configured roots exist and are readable by the process user (ls -la each root; check for EACCES/ENOENT in the retry error).
  3. Confirm watchman is installed and healthy: run `watchman version` and `watchman diagnose`; if broken, start it or recreate an empty `.watchmanconfig` in the project root.
  4. If watchman cannot be repaired, set haste-map option useWatchman:false (Jest config `watchman: false`) to skip the failing watchman path and crawl directly.
  5. Clear the haste-map cache (files under cacheDirectory matching haste-map-*) to rule out a corrupt cache confusing the crawler.

Example fix

// jest.config.js — before (watchman assumed, fallback also failing)
module.exports = { watchman: true };

// after — skip watchman so crawl() never enters the retry path
module.exports = { watchman: false };
Defensive patterns

Strategy: try-catch

Validate before calling

import {statSync} from 'node:fs';
// before calling HasteMap.create / build, verify every root exists & is readable
for (const root of options.roots) {
  const s = statSync(root); // throws ENOENT/EACCES — handle explicitly
  if (!(s.isDirectory())) throw new Error(`root is not a directory: ${root}`);
}
// optionally probe watchman up front
import isWatchmanInstalled from 'jest-haste-map/lib/isWatchmanInstalled';
const ok = await isWatchmanInstalled();
if (!ok) options.useWatchman = false;

Try / catch

try {
  const hasteMap = await HasteMap.create(opts);
  const data = await hasteMap.build();
} catch (err) {
  if (err.message.startsWith('Crawler retry failed:')) {
    // err.message contains both Original error and Retry error.
    // The Retry error is the actionable one. Surface it, do NOT retry identical.
    const retryError = err.message.split('Retry error:')[1]?.trim() ?? err.message;
    throw new Error(`Haste crawl unrecoverable — fix: ${retryError}`);
  }
  throw err;
}

Prevention

When it happens

Trigger: crawl() is invoked with useWatchman=true; watchmanCrawl(crawlerOptions) rejects; the .catch in crawlers/index.ts:29 invokes nodeCrawl(crawlerOptions) which also rejects, triggering `throw new Error('Crawler retry failed: ...')` at line 30. If useWatchman is false and nodeCrawl rejects, the original error is rethrown unchanged (line 38) — this combined message only appears via the watchman→node retry path.

Common situations: Watchman binary missing, hung, or crashed on a very large repo, combined with filesystem issues that also break the node fallback (EACCES/EACCES on roots, roots that do not exist, a full disk, an over-eager ignorePattern that hides everything, or a broken fdir/graceful-fs install). Common in CI containers where watchman is absent and the working directory is misconfigured.

Related errors


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