{"id":"4c46dc92206155c0","repo":"jestjs/jest","slug":"crawler-retry-failed-original-error-retryerr","errorCode":null,"errorMessage":"Crawler retry failed:\n  Original error: ${retryError.message}\n  Retry error: ${error.message}\n","messagePattern":"Crawler retry failed:\n  Original error: (.+?)\n  Retry error: (.+?)\n","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/jest-haste-map/src/crawlers/index.ts","lineNumber":30,"sourceCode":"export async function crawl(\n  crawlerOptions: CrawlerOptions,\n  useWatchman: boolean,\n  console: Console,\n): ReturnType<typeof nodeCrawl> {\n  const crawlFn = useWatchman ? watchmanCrawl : nodeCrawl;\n\n  const retry = (retryError: Error) => {\n    if (crawlFn === watchmanCrawl) {\n      console.warn(\n        'jest-haste-map: Watchman crawl failed. Retrying once with node ' +\n          'crawler.\\n' +\n          \"  Usually this happens when watchman isn't running. Create an \" +\n          \"empty `.watchmanconfig` file in your project's root folder or \" +\n          'initialize a git or hg repository in your project.\\n' +\n          `  ${retryError}`,\n      );\n      return nodeCrawl(crawlerOptions).catch(error => {\n        throw new Error(\n          'Crawler retry failed:\\n' +\n            `  Original error: ${retryError.message}\\n` +\n            `  Retry error: ${error.message}\\n`,\n        );\n      });\n    }\n\n    throw retryError;\n  };\n\n  try {\n    return await crawlFn(crawlerOptions);\n  } catch (error: any) {\n    return retry(error);\n  }\n}\n","sourceCodeStart":12,"sourceCodeEnd":47,"githubUrl":"https://github.com/jestjs/jest/blob/f49721c78e195558b40913977c9230f5b7f559d8/packages/jest-haste-map/src/crawlers/index.ts#L12-L47","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Read both messages: the 'Retry error' (second line) is the actionable one — fix that root cause first.","Verify all configured roots exist and are readable by the process user (ls -la each root; check for EACCES/ENOENT in the retry error).","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.","If watchman cannot be repaired, set haste-map option useWatchman:false (Jest config `watchman: false`) to skip the failing watchman path and crawl directly.","Clear the haste-map cache (files under cacheDirectory matching haste-map-*) to rule out a corrupt cache confusing the crawler."],"exampleFix":"// jest.config.js — before (watchman assumed, fallback also failing)\nmodule.exports = { watchman: true };\n\n// after — skip watchman so crawl() never enters the retry path\nmodule.exports = { watchman: false };","handlingStrategy":"try-catch","validationCode":"import {statSync} from 'node:fs';\n// before calling HasteMap.create / build, verify every root exists & is readable\nfor (const root of options.roots) {\n  const s = statSync(root); // throws ENOENT/EACCES — handle explicitly\n  if (!(s.isDirectory())) throw new Error(`root is not a directory: ${root}`);\n}\n// optionally probe watchman up front\nimport isWatchmanInstalled from 'jest-haste-map/lib/isWatchmanInstalled';\nconst ok = await isWatchmanInstalled();\nif (!ok) options.useWatchman = false;","typeGuard":null,"tryCatchPattern":"try {\n  const hasteMap = await HasteMap.create(opts);\n  const data = await hasteMap.build();\n} catch (err) {\n  if (err.message.startsWith('Crawler retry failed:')) {\n    // err.message contains both Original error and Retry error.\n    // The Retry error is the actionable one. Surface it, do NOT retry identical.\n    const retryError = err.message.split('Retry error:')[1]?.trim() ?? err.message;\n    throw new Error(`Haste crawl unrecoverable — fix: ${retryError}`);\n  }\n  throw err;\n}","preventionTips":["Validate roots exist and are readable before building the haste map.","Run `watchman version` in CI to fail fast when watchman is missing.","Set useWatchman:false in environments without a working watchman daemon to avoid the retry path entirely.","Keep an empty .watchmanconfig in the project root so watchman treats the project correctly."],"tags":["haste-map","watchman","crawler","filesystem","retry"],"analyzedSha":"f49721c78e195558b40913977c9230f5b7f559d8","analyzedAt":"2026-08-03T20:16:28.571Z","schemaVersion":2}