René's URL Explorer Experiment


Title: fs: rmSync with recursive returns without throwing while the directory still exists · Issue #67010 · nodejs/node · GitHub

Open Graph Title: fs: rmSync with recursive returns without throwing while the directory still exists · Issue #67010 · nodejs/node

X Title: fs: rmSync with recursive returns without throwing while the directory still exists · Issue #67010 · nodejs/node

Description: Version v24.22.1 and v26.12.1 (also v23.11.1 and v24.0.0). Not v22.23.4. Platform Linux 6.18.33.2-microsoft-standard-WSL2 x86_64 Subsystem fs What steps will reproduce the bug? Call fs.rmSync(dir, { recursive: true, force: true }) while ...

Open Graph Description: Version v24.22.1 and v26.12.1 (also v23.11.1 and v24.0.0). Not v22.23.4. Platform Linux 6.18.33.2-microsoft-standard-WSL2 x86_64 Subsystem fs What steps will reproduce the bug? Call fs.rmSync(dir, ...

X Description: Version v24.22.1 and v26.12.1 (also v23.11.1 and v24.0.0). Not v22.23.4. Platform Linux 6.18.33.2-microsoft-standard-WSL2 x86_64 Subsystem fs What steps will reproduce the bug? Call fs.rmSync(dir, ...

Opengraph URL: https://github.com/nodejs/node/issues/67010

X: @github

direct link

Domain: github.com


Hey, it has json ld scripts:
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"fs: rmSync with recursive returns without throwing while the directory still exists","articleBody":"### Version\n\nv24.22.1 and v26.12.1 (also v23.11.1 and v24.0.0). Not v22.23.4.\n\n### Platform\n\n```text\nLinux 6.18.33.2-microsoft-standard-WSL2 x86_64\n```\n\n### Subsystem\n\nfs\n\n### What steps will reproduce the bug?\n\nCall `fs.rmSync(dir, { recursive: true, force: true })` while another thread keeps creating and removing one file inside `dir`.\n\nThe script below does that. A worker churns a file under `dir/busy/`, the main thread calls `rmSync` on `dir`, and then checks whether `dir` still exists once the call has returned.\n\n```sh\nnode repro.mjs 300\n```\n\n\u003cdetails>\n\u003csummary>repro.mjs\u003c/summary>\n\n```js\n// fs.rmSync(dir, { recursive: true, force: true }) returns without throwing\n// while `dir` still exists, when another thread removes an entry inside `dir`\n// during the walk.\nimport { existsSync, mkdirSync, mkdtempSync, readdirSync, rmSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { Worker, isMainThread, workerData } from \"node:worker_threads\";\n\nif (!isMainThread) {\n  // Churn: keep creating and removing one file inside the tree being removed.\n  const { lock, stop } = workerData;\n  const flag = new Int32Array(stop);\n  while (Atomics.load(flag, 0) === 0) {\n    try {\n      writeFileSync(lock, \"\");\n      unlinkSync(lock);\n    } catch {}\n  }\n  process.exit(0);\n}\n\nconst TRIALS = Number(process.argv[2] ?? 200);\nconst counts = { removed: 0, threw: 0, survivedSilently: 0 };\nlet example;\n\nfor (let i = 0; i \u003c TRIALS; i++) {\n  const dir = mkdtempSync(join(tmpdir(), \"rmsync-\"));\n  const busy = join(dir, \"busy\");\n  mkdirSync(busy);\n  for (let d = 0; d \u003c 20; d++) {\n    mkdirSync(join(dir, `d${d}`));\n    for (let f = 0; f \u003c 20; f++) writeFileSync(join(dir, `d${d}`, `f${f}`), \"x\");\n  }\n  const stop = new SharedArrayBuffer(4);\n  const worker = new Worker(new URL(import.meta.url), { workerData: { lock: join(busy, \"lock\"), stop } });\n  await new Promise((resolve) => worker.once(\"online\", resolve));\n\n  try {\n    rmSync(dir, { recursive: true, force: true });\n    if (existsSync(dir)) {\n      counts.survivedSilently++;\n      example ??= readdirSync(dir, { recursive: true });\n    } else {\n      counts.removed++;\n    }\n  } catch (error) {\n    counts.threw++;\n  }\n\n  Atomics.store(new Int32Array(stop), 0, 1);\n  await new Promise((resolve) => worker.once(\"exit\", resolve));\n  rmSync(dir, { recursive: true, force: true, maxRetries: 5 });\n}\n\nconsole.log(`node ${process.version}`, JSON.stringify(counts));\nif (example) console.log(\"left behind after rmSync returned:\", JSON.stringify(example));\n```\n\n\u003c/details>\n\n### How often does it reproduce? Is there a required condition?\n\nRoughly one call in five on this machine. Out of 300 calls, 65 on v24.22.1 and 61 on v26.12.1 returned with the directory still there. On v22.23.4 it was 0.\n\nIt needs another process or thread removing an entry inside the tree while `rmSync` walks it. Any program that removes a directory while something still writes into it can hit this. I found it through `git commit`. Since git 2.47 it leaves a detached `git maintenance` process touching `.git/objects` after the command returns, and a test that removed its fixture repo right after committing sometimes kept the repo.\n\n### What is the expected behavior? Why is that the expected behavior?\n\nEither `dir` is gone when `rmSync` returns, or `rmSync` throws.\n\nThe docs for `force` say a missing path is ignored, and `maxRetries` lists the errors that are retried. Nothing allows a successful return with the path still there. v22 removed the rest of the tree in this situation. Its JS implementation skipped an entry that had vanished and carried on.\n\n### What do you see instead?\n\n`rmSync` returns normally and `dir` is still on disk, partly removed:\n\n```text\nnode v24.22.1 {\"removed\":196,\"threw\":39,\"survivedSilently\":65}\nleft behind after rmSync returned: [\"busy\",\"d1\",\"d10\",...,\"busy/lock\",\"d1/f0\",...]\n```\n\n### Additional information\n\n`RmSync` in `src/node_file.cc` calls `std::filesystem::remove_all` and treats `no_such_file_or_directory` as success:\n\n```cpp\nif (!error || error == std::errc::no_such_file_or_directory) {\n  return;\n}\n```\n\n`remove_all` reports that error for an entry that disappeared during the walk as well as for a missing target, and it stops at that entry. Only the missing target means the path is gone. This came in with the C++ implementation in #53617, so v23 onward.\n\n[Proposed fix here\n](https://github.com/nodejs/node/pull/67011)","author":{"url":"https://github.com/arieller","@type":"Person","name":"arieller"},"datePublished":"2026-10-26T05:31:26Z","url":"https://github.com/nodejs/node/issues/67010"}

react-profiling0
react-import-mapreact,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@d5750093facf
route-pattern/_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format)
route-controllervoltron_issues_fragments
route-actionissue_layout
fetch-noncev2:c7a41558-a6c6-90f2-d872-f33cd62ae5d2
current-catalog-service-hash81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114
request-idB7EC:3BA6B9:C02C58:1004763:6AED3C48
html-safe-nonce0e7335a5a412e2e521d075ab203718048cd62bf5f857ebebfe9bfa7c65454b34
visitor-payloadeyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJCN0VDOjNCQTZCOTpDMDJDNTg6MTAwNDc2Mzo2QUVEM0M0OCIsInZpc2l0b3JfaWQiOiI5Njg3MzkyNzI3OTUxNzYwMDgiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ==
visitor-hmac384dd4a4dcc5863fa2dfbd58e6808f6343469fe878421be1421501e7c2b84cca
hovercard-subject-tagissue:6033952263
github-keyboard-shortcutsrepository,issues,copilot
google-site-verificationApib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I
octolytics-urlhttps://collector.github.com/github/collect
analytics-location///voltron/issues_fragments/issue_layout
fb:app_id1401488693436528
apple-itunes-appapp-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/nodejs/node/67010/issue_layout
twitter:imagehttps://opengraph.githubassets.com/82fe86441e5e5685794f341672380c49a6f634f948111be9144284b10cbf48b6/nodejs/node/issues/67010
twitter:cardsummary_large_image
og:imagehttps://opengraph.githubassets.com/82fe86441e5e5685794f341672380c49a6f634f948111be9144284b10cbf48b6/nodejs/node/issues/67010
og:image:altVersion v24.22.1 and v26.12.1 (also v23.11.1 and v24.0.0). Not v22.23.4. Platform Linux 6.18.33.2-microsoft-standard-WSL2 x86_64 Subsystem fs What steps will reproduce the bug? Call fs.rmSync(dir, ...
og:image:width1200
og:image:height600
og:site_nameGitHub
og:typeobject
og:author:usernamearieller
hostnamegithub.com
expected-hostnamegithub.com
None26c92a9388ddfad3fa88d132b5d5f0b4747030233dcc4e5efb05f1cbae152184
turbo-cache-controlno-preview
go-importgithub.com/nodejs/node git https://github.com/nodejs/node.git
octolytics-dimension-user_id9950313
octolytics-dimension-user_loginnodejs
octolytics-dimension-repository_id27193779
octolytics-dimension-repository_nwonodejs/node
octolytics-dimension-repository_publictrue
octolytics-dimension-repository_is_forkfalse
octolytics-dimension-repository_network_root_id27193779
octolytics-dimension-repository_network_root_nwonodejs/node
turbo-body-classeslogged-out env-production page-responsive
disable-turbofalse
browser-stats-urlhttps://api.github.com/_private/browser/stats
browser-errors-urlhttps://api.github.com/_private/browser/errors
releasec629ad99c254d306375fc8354d04321c2c362236
ui-targetfull
theme-color#1e2327
color-schemelight dark
react-app-nameissues

Links:

Skip to contenthttps://github.com/nodejs/node/issues/67010#start-of-content
Reloadhttps://github.com/nodejs/node/issues/67010
Reloadhttps://github.com/nodejs/node/issues/67010
Reloadhttps://github.com/nodejs/node/issues/67010
nodejshttps://github.com/nodejs
nodehttps://github.com/nodejs/node
Codehttps://github.com/nodejs/node
Issues656 (656)https://github.com/nodejs/node/issues
Pull requests665 (665)https://github.com/nodejs/node/pulls
Actionshttps://github.com/nodejs/node/actions
Projectshttps://github.com/nodejs/node/projects
Security and qualityhttps://github.com/nodejs/node/security
Insightshttps://github.com/nodejs/node/pulse
https://github.com/nodejs/node
New issuehttps://github.com/login?return_to=https://github.com/nodejs/node/issues/67010
New issuehttps://github.com/login?return_to=https://github.com/nodejs/node/issues/67010
fs: rmSync with recursive returns without throwing while the directory still existshttps://github.com/nodejs/node/issues/67010#top
https://github.com/arieller
ariellerhttps://github.com/arieller
on Oct 26, 2026https://github.com/nodejs/node/issues/67010#issue-6033952263
#53617https://github.com/nodejs/node/pull/53617
Proposed fix here https://github.com/nodejs/node/pull/67011
ariellerhttps://github.com/arieller
on Oct 26, 2026https://github.com/nodejs/node/issues/67010#event-34227465604
fs: finish rmSync when an entry vanishes during the walkhttps://github.com/arieller/node/commit/3063dfde2862996735891f84af953375d9a07fae
3063dfdhttps://github.com/arieller/node/commit/3063dfde2862996735891f84af953375d9a07fae
Sign up for freehttps://github.com/signup?return_to=https://github.com/nodejs/node/issues/67010
Sign in to commenthttps://github.com/login?return_to=https://github.com/nodejs/node/issues/67010
https://github.com
Termshttps://docs.github.com/site-policy/github-terms/github-terms-of-service
Privacyhttps://docs.github.com/site-policy/privacy-policies/github-privacy-statement
Securityhttps://github.com/security
Statushttps://www.githubstatus.com/
Communityhttps://github.community/
Docshttps://docs.github.com/
Contacthttps://support.github.com?tags=dotcom-footer

Viewport: width=device-width


URLs of crawlers that visited me.