jestjs/jest · error · Error
Crawler retry failed: Original error
Error message
Crawler retry failed:
Original error: ${retryError.message}
Retry error: ${error.message}
What it means
jest-haste-map's file-system crawler first tries watchman; if that fails it retries once with the node (fdir/find) crawler. This error is the terminal failure: BOTH the watchman crawl and the fallback node crawl raised exceptions. The combined message shows the original watchman error and the retry node-crawler error so you can diagnose both. It means the entire haste-map build aborts — no module map is produced.
Solutions
- Run `watchman version` and `watchman shutdown-server` to reset a stale watchman daemon, then rebuild.
- Verify every path in Jest's `roots` / `moduleDirectories` / `testPathIgnorePatterns` resolves and is readable: `ls -la <root>` for each root.
- If watchman is the culprit, set `haste: { forceNodeFilesystemAPI: true }` or pass `useWatchman: false` to skip watchman entirely.
- Check the two nested messages (Original error / Retry error) for ENOENT/EACCES codes and fix the named path's permissions or remove it from roots.
Example fix
// before
module.exports = {
roots: ['<rootDir>/src', '<rootDir>/packages/legacy'],
};
// after — legacy path no longer exists; removed it
module.exports = {
roots: ['<rootDir>/src'],
}; Defensive patterns
Strategy: validation
Validate before calling
// Before building, verify every root exists and is readable.
import { accessSync, constants } from 'node:fs';
function validateRoots(roots: string[]): void {
for (const root of roots) {
try {
accessSync(root, constants.R_OK);
} catch {
throw new Error(`jest-haste-map root is not readable: ${root}`);
}
}
}
// Call before HasteMap.create(...)
validateRoots(config.roots); Type guard
function areRootsAccessible(roots: string[]): boolean {
return roots.every(root => {
try { accessSync(root, constants.R_OK); return true; }
catch { return false; }
});
} Try / catch
try {
await hasteMap.build();
} catch (error) {
if (error.message.startsWith('Crawler retry failed')) {
// Inspect error.message for both Original and Retry errors,
// then either fix paths or disable watchman.
console.error('Crawl failed for both backends. Root cause:\n', error.message);
}
throw error;
} Prevention
- Keep watchman healthy: run `watchman version` in CI before Jest.
- Validate all configured roots exist at startup (accessSync).
- Prefer `useWatchman: false` in CI where the node crawler is more predictable.
- Avoid pointing roots at directories that branches may delete.
When it happens
Trigger: crawl() catches the watchmanCrawl error, enters the retry path (crawlFn === watchmanCrawl), calls nodeCrawl(crawlerOptions), and nodeCrawl also rejects — e.g. due to EACCES/ENOENT on a root directory, a corrupt filesystem entry, or an unreadable file during the fdir walk. Any exception from nodeCrawl is caught and re-thrown as this combined Error.
Common situations: Watchman is installed but misconfigured or its socket is stale, AND a root directory the project points at does not exist or has restrictive permissions (CI containers, Docker mounts, monorepo path typos). Also seen after a branch switch leaves dangling paths referenced by the Jest config roots, or when a symlink loop exists and forceNodeFilesystemAPI is off.
Related errors
- Failed to start watch mode.
- jest-haste-map: enableSymlinks config option was set, but…
- Attempted to display seed but seed value is undefined
- babel-jest: Babel ignores
- Can't find a root directory while resolving a config file…
AI-assisted analysis of jestjs/jest@8e6d128e4a (2026-08-10).
Data as JSON: /api/errors/4c46dc92206155c0.
Report an issue: GitHub.
Appendix: 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 8e6d128e4a)