René's URL Explorer Experiment


Title: `fs.mkdir` with `recursive: true` retries without a progress bound: spins forever on `/proc`, livelocks under concurrent `rmdir` · Issue #66268 · nodejs/node · GitHub

Open Graph Title: `fs.mkdir` with `recursive: true` retries without a progress bound: spins forever on `/proc`, livelocks under concurrent `rmdir` · Issue #66268 · nodejs/node

X Title: `fs.mkdir` with `recursive: true` retries without a progress bound: spins forever on `/proc`, livelocks under concurrent `rmdir` · Issue #66268 · nodejs/node

Description: Version v22.23.3 and v24.21.0 (both reproduce) Platform Linux 6.8.0-100-generic #100-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan 13 16:39:21 UTC 2026 aarch64 GNU/Linux Official node:22 and node:24 images (Debian 12 userland). /proc is an ordinary...

Open Graph Description: Version v22.23.3 and v24.21.0 (both reproduce) Platform Linux 6.8.0-100-generic #100-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan 13 16:39:21 UTC 2026 aarch64 GNU/Linux Official node:22 and node:24 images (D...

X Description: Version v22.23.3 and v24.21.0 (both reproduce) Platform Linux 6.8.0-100-generic #100-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan 13 16:39:21 UTC 2026 aarch64 GNU/Linux Official node:22 and node:24 images (D...

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

X: @github

direct link

Domain: github.com


Hey, it has json ld scripts:
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"`fs.mkdir` with `recursive: true` retries without a progress bound: spins forever on `/proc`, livelocks under concurrent `rmdir`","articleBody":"### Version\n\nv22.23.3 and v24.21.0 (both reproduce)\n\n### Platform\n\n```text\nLinux 6.8.0-100-generic #100-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan 13 16:39:21 UTC 2026 aarch64 GNU/Linux\n```\n\nOfficial `node:22` and `node:24` images (Debian 12 userland). `/proc` is an ordinary procfs mount with\ndefault options: `proc /proc proc rw,nosuid,nodev,noexec,relatime`.\n\nAlso reproduced independently on x86_64, on GitHub-hosted `ubuntu-latest` runners with Node 22 and 24,\nwhere the hung job hit the 6-hour ceiling twice.\n\n### Subsystem\n\nfs\n\n### What steps will reproduce the bug?\n\n```console\n$ node -e \"require('node:fs').mkdirSync('/proc/zzz', { recursive: true })\"\n```\n\nNo output, no error, 100% CPU until killed. `fs.promises.mkdir` and the callback form of `fs.mkdir`\nbehave the same way. Dropping `recursive: true` returns `ENOENT` immediately.\n\nA second reproduction needs no pseudo-filesystem. On an ordinary disk filesystem (I used ext4; `T`\nbelow is a directory on it, not a tmpfs or overlayfs), run this loop:\n\n```js\nconst fs = require('node:fs');\nconst p = `${T}/parent/child`;\nfor (let i = 0; i \u003c 5000; i++) {\n  fs.mkdirSync(p, { recursive: true });\n  fs.rmSync(p, { recursive: true, force: true });\n}\n```\n\nwhile N other processes spin on `fs.rmdirSync(T + '/parent/child')` and\n`fs.rmdirSync(T + '/parent')`. Latency per call degrades with N:\n\n| concurrent removers | mean per call | worst call | calls over 50ms (of 5000) |\n|---|---|---|---|\n| 0 | 0.006ms | 0.1ms | 0 |\n| 8 | 0.158ms | 14.8ms | 0 |\n| 32 | 2.115ms | 168ms | 93 |\n\n(4 vCPUs. Racers must issue the `rmdir` syscall directly; shell `rmdir(1)` racers are too slow to win\nthe race because of fork+exec overhead.)\n\n### How often does it reproduce? Is there a required condition?\n\nThe `/proc` case is 100% reproducible on Linux with no special conditions or privileges. It does not\nreproduce on macOS or Windows, which have no `/proc`.\n\nThe concurrent-removal case is timing-dependent and scales with contention. The window is narrow on\nfast local storage, so it shows up as latency rather than a permanent hang at the numbers above.\nWidening the window by running the `mkdir` side under `strace` drove it to 67 `mkdirat` syscalls per\nattempted `mkdirp`, sustained, and 2000 iterations did not finish inside a 60-second timeout.\n\n### What is the expected behavior? Why is that the expected behavior?\n\n`mkdirSync(p, { recursive: true })` should either create the directory or fail with an errno, in\nbounded time. `mkdir -p` and `mkdirSync` without `recursive` both do: the latter returns `ENOENT`\nimmediately for `/proc/zzz`. Recursive mode should not be able to turn a failing `mkdir` into an\nunbounded loop.\n\n### What do you see instead?\n\nAn unbounded retry loop. `strace -f -e trace=mkdir,mkdirat` over 5 seconds of the `/proc` case shows\n22,872 `mkdirat` calls in a repeating 2-call cycle:\n\n```\nmkdirat(AT_FDCWD, \"/proc/zzz\", 0777) = -1 ENOENT (No such file or directory)\nmkdirat(AT_FDCWD, \"/proc\", 0777)     = -1 EEXIST (File exists)\nmkdirat(AT_FDCWD, \"/proc/zzz\", 0777) = -1 ENOENT (No such file or directory)\nmkdirat(AT_FDCWD, \"/proc\", 0777)     = -1 EEXIST (File exists)\n...\n```\n\n### Additional information\n\n### Mechanism\n\n`MKDirpSync` in [`src/node_file.cc`](https://github.com/nodejs/node/blob/v24.x/src/node_file.cc#L1874-L1904)\ntreats `ENOENT` as \"the parent does not exist yet\", pushes the failed path back onto its stack, pushes\nthe parent ahead of it, and retries:\n\n```c++\ncase UV_ENOENT: {\n  std::string dirname =\n      next_path.substr(0, next_path.find_last_of(kPathSeparator));\n  if (dirname != next_path) {\n    req_wrap->continuation_data()->PushPath(std::move(next_path));\n    req_wrap->continuation_data()->PushPath(std::move(dirname));\n  } else if (req_wrap->continuation_data()->paths().empty()) {\n    err = UV_EEXIST;\n    continue;\n  }\n  break;\n}\n```\n\n`ENOENT` is the only errno that re-queues work. `EACCES`, `ENOSPC`, `ENOTDIR` and `EPERM` return\nimmediately, and everything else reaches `default:`, where a failing `stat` ends the walk. So the loop\nterminates only where `mkdir` returning `ENOENT` implies a missing parent, and POSIX does not\nguarantee that.\n\nTwo cases where it does not hold:\n\n* Under `/proc`, `mkdir` returns `ENOENT` for a name that can never be created, even though the parent\n  exists. Each cycle pops `/proc/zzz` (`ENOENT`), pushes it back with `/proc`, pops `/proc`\n  (`EEXIST`, `stat` confirms a directory, falls through), and finds `/proc/zzz` still on the stack.\n* Under concurrent removal, the `ENOENT` is real but transient: a peer removed the parent after the\n  last check, so the retry recreates it, and the child create can miss again.\n\nNeither `MKDirpSync` nor `MKDirpAsync` records whether an iteration made progress, so there is no\nstate that can end the loop.\n\nFor contrast, `/sys` does not hang: `mkdir` there fails with `EROFS`, which reaches `default:` and\nterminates. `/etc/hostname/zzz` returns `ENOTDIR`. Only the `ENOENT` path re-queues.\n\n### Impact\n\nA recursive `mkdir` on a path the caller does not fully control, or one contended by another process,\ncan go from microseconds to unbounded. On the sync path the spin is inside a C++ call, so no JS\ntimeout, `AbortSignal` or signal handler interrupts it; only `SIGKILL` from outside the process ends\nit. Running the one-liner under `docker run` and pressing Ctrl-C three times is enough to see this:\nthe CLI gives up forwarding signals and detaches, and the container keeps running and keeps spinning.\nThe async path occupies a libuv threadpool worker the same way.\n\nI hit the `/proc` case in CI, where a test used a path under `/proc` as an unwritable fixture. Nothing\ncrashed and nothing was logged, so the run was killed only by GitHub Actions' own 6-hour job limit. I\ndid not notice the hang itself: what surfaced was every later job being refused for billing, because\nthose 6 hours had exhausted the account's Actions budget. The test had always passed on macOS, where\n`/proc` does not exist.\n\n### Possible fix\n\nBound the retry on lack of progress. If a path is popped a second time with no successful `mkdir` in\nbetween, return the original error rather than pushing its parent again. The uncontended path stays as\ncheap as it is today, and both `MKDirpSync` and `MKDirpAsync` then terminate regardless of what a\nfilesystem returns.\n\nA retry cap would also stop the race case, but it leaves `/proc` looping unless the cap is small\nenough to apply there too, which makes it the same fix with a number attached.\n\nI am happy to open a PR with tests for both cases if that direction seems right.\n\n### Related\n\nTwo earlier reports of this loop, both on Windows, both fixed by correcting one filesystem's errno\nrather than by bounding the loop:\n\n* #27198, fixed by #27207 (\"fs: fix infinite loop with async recursive mkdir on Windows\").\n* #28599, recursive `mkdir` hanging on invalid Windows filenames. The diagnosis there matches:\n  `uv_fs_mkdir` returns `UV_ENOENT`, which \"confuses the `fs.mkdir` function. It constantly creates\n  the parent directory then tries to create the invalid folder.\"\n\nAI-assisted. Reproduced and measured in a Linux container before filing; the one-liner cannot\nreproduce on macOS, which has no `/proc`.\n","author":{"url":"https://github.com/rndjams","@type":"Person","name":"rndjams"},"datePublished":"2026-09-24T23:18:23Z","url":"https://github.com/nodejs/node/issues/66268"}

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:51c2b9c3-7522-8243-7fa7-445ef509d839
current-catalog-service-hash81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114
request-idD80A:10B16D:9EA946:D32722:6AED316D
html-safe-nonce0450e747e192c58f79460e426ee27d8d7576c10a48a4e66c88b5997af3790c01
visitor-payloadeyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJEODBBOjEwQjE2RDo5RUE5NDY6RDMyNzIyOjZBRUQzMTZEIiwidmlzaXRvcl9pZCI6Ijg2NzAzNTA0MzQ3MDgwNDIwOTMiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ==
visitor-hmacb552a56500884e2e91d59f8efa2f7f614a83f5eb8380c256778ad9badd0c8b6e
hovercard-subject-tagissue:5575784932
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/66268/issue_layout
twitter:imagehttps://opengraph.githubassets.com/8dc7263849c102dc49cc4a3dfacc75612ea128bcc957207a4b2cac0709c5dae7/nodejs/node/issues/66268
twitter:cardsummary_large_image
og:imagehttps://opengraph.githubassets.com/8dc7263849c102dc49cc4a3dfacc75612ea128bcc957207a4b2cac0709c5dae7/nodejs/node/issues/66268
og:image:altVersion v22.23.3 and v24.21.0 (both reproduce) Platform Linux 6.8.0-100-generic #100-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan 13 16:39:21 UTC 2026 aarch64 GNU/Linux Official node:22 and node:24 images (D...
og:image:width1200
og:image:height600
og:site_nameGitHub
og:typeobject
og:author:usernamerndjams
hostnamegithub.com
expected-hostnamegithub.com
Nonee51d0b7b5ab34b1688c05b54c3ed17b8658b2fa9bca973a9c740683ccb12cf4a
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
release59ebb7b901cc154ecc179a44f266fcf69f343b30
ui-targetfull
theme-color#1e2327
color-schemelight dark
react-app-nameissues

Links:

Skip to contenthttps://github.com/nodejs/node/issues/66268#start-of-content
https://github.com/
Sign inhttps://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fnodejs%2Fnode%2Fissues%2F66268
GitHubRepository hosting, code review, and collaborationhttps://github.com/platform
CopilotAI coding agent for tasks, projects, and automated workhttps://github.com/features/copilot
ActionsCI/CD and automationhttps://github.com/features/actions
Code QualityQuality analysis, fixes, and enforcementhttps://github.com/features/code-quality
CodespacesCloud development environmentshttps://github.com/features/codespaces
Code SecurityVulnerability detection and remediationhttps://github.com/security/advanced-security/code-security
Secret ProtectionSecret scanning and leak preventionhttps://github.com/security/advanced-security/secret-protection
All Featureshttps://github.com/features
Documentationhttps://docs.github.com/en
Traininghttps://learn.github.com/skills
Certificationshttps://learn.github.com/credentials
Build with Copilothttps://github.com/features/ai
Customize Copilothttps://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/customize-copilot-overview
Integrationshttps://github.com/marketplace
Trending reposhttps://github.com/trending
GitHub Sponsorshttps://github.com/open-source/sponsors
Developer articleshttps://github.com/resources/articles
ChangelogYour source for the latest features, improvements, and fixes across GitHub products, all in one place.https://github.blog/changelog/
Enterpriseshttps://github.com/enterprise
Teamhttps://github.com/team
Startupshttps://github.com/enterprise/startups
Nonprofitshttps://github.com/solutions/industry/nonprofits
Healthcarehttps://github.com/solutions/industry/healthcare
Financial serviceshttps://github.com/solutions/industry/financial-services
Manufacturinghttps://github.com/solutions/industry/manufacturing
Governmenthttps://github.com/solutions/industry/government
See all industrieshttps://github.com/solutions/industry
Why GitHubhttps://github.com/why-github
Customer Storieshttps://github.com/customer-stories
Business insightshttps://github.com/solutions/executive-insights
Trust Centerhttps://github.com/trust-center
GitHub Advanced SecurityEnterprise-grade code securityhttps://github.com/security/advanced-security
Copilot for businessEnterprise-grade AI featureshttps://github.com/features/copilot/copilot-business
Premium SupportEnterprise-grade 24/7 supporthttps://github.com/enterprise/premium-support
Documentationhttps://docs.github.com/en
Resource centerhttps://github.com/resources
Bloghttps://github.blog/
Events & webinarshttps://github.com/resources/events
Community Forumhttps://github.com/orgs/community/discussions
Customer Storieshttps://github.com/customer-stories
Integrationshttps://github.com/marketplace
Customer supporthttps://help.github.com/
Traininghttps://learn.github.com/skills
GitHub Statushttps://www.githubstatus.com/
Abouthttps://github.com/about
Partnershttps://github.com/partners
Changeloghttps://github.blog/changelog/
GitHub UniverseThe event for developers, by developers — talks, workshops, and what’s next for GitHub.https://githubuniverse.com/
Pricinghttps://github.com/pricing
Sign inhttps://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fnodejs%2Fnode%2Fissues%2F66268
Sign uphttps://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fvoltron%2Fissues_fragments%2Fissue_layout&source=header-repo&source_repo=nodejs%2Fnode
Reloadhttps://github.com/nodejs/node/issues/66268
Reloadhttps://github.com/nodejs/node/issues/66268
Reloadhttps://github.com/nodejs/node/issues/66268
nodejshttps://github.com/nodejs
nodehttps://github.com/nodejs/node
Codehttps://github.com/nodejs/node
Issues656 (656)https://github.com/nodejs/node/issues
Pull requests666 (666)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/66268
New issuehttps://github.com/login?return_to=https://github.com/nodejs/node/issues/66268
#66340https://github.com/nodejs/node/pull/66340
fs.mkdir with recursive: true retries without a progress bound: spins forever on /proc, livelocks under concurrent rmdirhttps://github.com/nodejs/node/issues/66268#top
#66340https://github.com/nodejs/node/pull/66340
https://github.com/rndjams
rndjamshttps://github.com/rndjams
on Sep 24, 2026https://github.com/nodejs/node/issues/66268#issue-5575784932
src/node_file.cchttps://github.com/nodejs/node/blob/v24.x/src/node_file.cc#L1874-L1904
mkdir no ERROR when parent is a file with native recursive option #27198https://github.com/nodejs/node/issues/27198
fs: fix infinite loop with async recursive mkdir on Windows #27207https://github.com/nodejs/node/pull/27207
fs.mkdir/mkdirSync hang with {recursive: true} if name is invalid #28599https://github.com/nodejs/node/issues/28599
marcopiraccinihttps://github.com/marcopiraccini
on Sep 27, 2026https://github.com/nodejs/node/issues/66268#event-31922780773
fs: stop recursive mkdir from retrying ENOENT foreverhttps://github.com/marcopiraccini/node/commit/f8330e116d636712a3285a17063305dcddc3c44c
f8330e1https://github.com/marcopiraccini/node/commit/f8330e116d636712a3285a17063305dcddc3c44c
fs: stop recursive mkdir from retrying ENOENT foreverhttps://github.com/marcopiraccini/node/commit/03cf2682b08fb3d24e38fa8b5ca37a9d14689abd
03cf268https://github.com/marcopiraccini/node/commit/03cf2682b08fb3d24e38fa8b5ca37a9d14689abd
agape1225https://github.com/agape1225
on Sep 30, 2026https://github.com/nodejs/node/issues/66268#event-32153836185
fs: bound the mkdir recursive ENOENT retry loophttps://github.com/agape1225/node/commit/1fb3404315944704c402ed78779191cf451ce89e
1fb3404https://github.com/agape1225/node/commit/1fb3404315944704c402ed78779191cf451ce89e
fs: bound the mkdir recursive ENOENT retry loophttps://github.com/agape1225/node/commit/b19049af20772c94c13dda5c327b3abf46745e0b
b19049ahttps://github.com/agape1225/node/commit/b19049af20772c94c13dda5c327b3abf46745e0b
nodejs-github-bothttps://github.com/nodejs-github-bot
completedhttps://github.com/nodejs/node/issues?q=is%3Aissue%20state%3Aclosed%20archived%3Afalse%20reason%3Acompleted
69f3ae9https://github.com/nodejs/node/commit/69f3ae9e8a66e601acde539f7df8690f0d41653f
on Sep 30, 2026https://github.com/nodejs/node/issues/66268#event-32169061065
pullhttps://github.com/apps/pull
on Sep 30, 2026https://github.com/nodejs/node/issues/66268#event-32169309077
fs: stop recursive mkdir from retrying ENOENT foreverhttps://github.com/edisplay/node/commit/69f3ae9e8a66e601acde539f7df8690f0d41653f
69f3ae9https://github.com/edisplay/node/commit/69f3ae9e8a66e601acde539f7df8690f0d41653f
aduh95https://github.com/aduh95
on Oct 3, 2026https://github.com/nodejs/node/issues/66268#event-32398857087
fs: stop recursive mkdir from retrying ENOENT foreverhttps://github.com/nodejs/node/commit/2b2a27b9ccd73ebf47feb28acbbe44a76fa1d982
2b2a27bhttps://github.com/nodejs/node/commit/2b2a27b9ccd73ebf47feb28acbbe44a76fa1d982
fs: stop recursive mkdir from retrying ENOENT foreverhttps://github.com/nodejs/node/commit/e7121be4083c04a69a316970e5490594d2f1542e
e7121behttps://github.com/nodejs/node/commit/e7121be4083c04a69a316970e5490594d2f1542e
aduh95https://github.com/aduh95
on Oct 23, 2026https://github.com/nodejs/node/issues/66268#event-34094415201
fs: stop recursive mkdir from retrying ENOENT foreverhttps://github.com/nodejs/node/commit/717207a772de3adb6d4acf34c8f20c1718cba834
717207ahttps://github.com/nodejs/node/commit/717207a772de3adb6d4acf34c8f20c1718cba834
fs: stop recursive mkdir from retrying ENOENT foreverhttps://github.com/nodejs/node/commit/1a1eb84020a35ab5737e09f079885d18950744fe
1a1eb84https://github.com/nodejs/node/commit/1a1eb84020a35ab5737e09f079885d18950744fe
Sign up for freehttps://github.com/signup?return_to=https://github.com/nodejs/node/issues/66268
Sign in to commenthttps://github.com/login?return_to=https://github.com/nodejs/node/issues/66268
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.