Title: AIX: clang libc++ std::filesystem::remove_all() Returns Wrong Error Code · Issue #62790 · nodejs/node · GitHub
Open Graph Title: AIX: clang libc++ std::filesystem::remove_all() Returns Wrong Error Code · Issue #62790 · nodejs/node
X Title: AIX: clang libc++ std::filesystem::remove_all() Returns Wrong Error Code · Issue #62790 · nodejs/node
Description: Summary On AIX with clang/libc++, std::filesystem::remove_all() incorrectly returns error code 17 (EEXIST - "File exists") instead of 13 (EACCES - "Permission denied") when encountering permission errors during recursive directory remova...
Open Graph Description: Summary On AIX with clang/libc++, std::filesystem::remove_all() incorrectly returns error code 17 (EEXIST - "File exists") instead of 13 (EACCES - "Permission denied") when encountering permission ...
X Description: Summary On AIX with clang/libc++, std::filesystem::remove_all() incorrectly returns error code 17 (EEXIST - "File exists") instead of 13 (EACCES - "Permission denied") when enco...
Opengraph URL: https://github.com/nodejs/node/issues/62790
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"AIX: clang libc++ std::filesystem::remove_all() Returns Wrong Error Code","articleBody":"\n## Summary\nOn AIX with clang/libc++, `std::filesystem::remove_all()` incorrectly returns error code **17 (EEXIST - \"File exists\")** instead of **13 (EACCES - \"Permission denied\")** when encountering permission errors during recursive directory removal. This doesn't happen using gcc/libstdc++ on the same machine.\n**Note**: This bug only manifests when running as a **non-root user**. When run as root, the operation succeeds (no permission error occurs), so the bug is not triggered.\n\n## Environment\n\n### AIX (Bug Present)\n- **OS**: AIX 7.2\n- **Compiler**: clang version 20.1.7 (powerpc-ibm-aix7.2.0.0)\n- **Target**: powerpc-ibm-aix7.2.0.0\n- **Standard Library**: libc++\n\n### AIX with GCC (Correct Behavior)\n- **OS**: AIX 7.2\n- **Compiler**: GCC 12.3.0\n- **Standard Library**: libstdc++\n- **Result**: Bug does NOT occur with GCC on AIX\n\nAlso tested behavior on Linux:\n\n### Linux (Correct Behavior)\n- **OS**: Fedora 43\n- **Compiler**: clang version 20.1.8 (Fedora 20.1.8-2.fc43)\n- **Target**: x86_64-redhat-linux-gnu\n- **Standard Library**: libc++\n\n\n## Expected Behavior\nWhen `std::filesystem::remove_all()` fails due to insufficient permissions:\n- `error.value()` should be **13** (EACCES)\n- `error.message()` should be **\"Permission denied\"**\n- `error == std::errc::permission_denied` should be **true**\n\n## Actual Behavior on AIX\n- `error.value()` is **17** (EEXIST)\n- `error.message()` is **\"File exists\"**\n- `error == std::errc::permission_denied` is **false**\n\n**Note**: This bug only manifests when running as a **non-root user**. When run as root, the operation succeeds (no permission error occurs), so the bug is not triggered.\n\n## Impact\nThis bug causes applications to misinterpret permission errors as \"file exists\" errors, leading to incorrect error handling.\n\nThe Node.js test suite fails on AIX with clang due to this bug:\n\n**Test**: `parallel/test-fs-rm` (test case 1918)\n\n**Failure**:\n```\nnot ok 1918 parallel/test-fs-rm\n\nAssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:\n+ actual - expected\n\n Comparison {\n+ code: '',\n- code: 'EACCES',\n name: 'Error'\n }\n\nactual: Error: , Unknown error: File exists '/home/iojs/build/.tmp.1724/rm-11/fs-KFNaea'\n at Object.rmSync (node:fs:1206:18)\n ...\n {\n errno: -4094,\n code: '',\n path: '/home/iojs/build/.tmp.1724/rm-11/fs-KFNaea',\n syscall: 'rm'\n },\n expected: { code: 'EACCES', name: 'Error' }\n```\n\nref: https://ci.nodejs.org/job/node-test-commit-aix-abmusse/21/nodes=test-ibm-aix72-ppc64_be-2/consoleText\n\n## Reproducer\n\n### Test Program\nSave the following as `test-clang-fs-removeall-bug.cpp`:\n\n```cpp\n/*\n * Bug: std::filesystem::remove_all() returns EEXIST instead of EACCES on AIX\n *\n * Expected: error.value() == 13 (EACCES - Permission denied)\n * Actual on AIX: error.value() == 17 (EEXIST - File exists)\n */\n\n#include \u003ciostream\u003e\n#include \u003cfilesystem\u003e\n#include \u003csystem_error\u003e\n#include \u003csys/stat.h\u003e\n#include \u003cerrno.h\u003e\n\nint main() {\n // Create directory structure: root/middle/leaf\n std::filesystem::create_directories(\"root/middle/leaf\");\n \n // Make middle read-only to trigger permission error\n chmod(\"root/middle\", 0555);\n \n // Try to remove_all (should fail with permission error)\n std::error_code error;\n std::filesystem::remove_all(\"root\", error);\n \n // Report results\n std::cout \u003c\u003c \"EACCES = \" \u003c\u003c EACCES \u003c\u003c std::endl;\n std::cout \u003c\u003c \"EEXIST = \" \u003c\u003c EEXIST \u003c\u003c std::endl;\n std::cout \u003c\u003c \"error.value() = \" \u003c\u003c error.value() \u003c\u003c std::endl;\n std::cout \u003c\u003c \"error.message() = \\\"\" \u003c\u003c error.message() \u003c\u003c \"\\\"\" \u003c\u003c std::endl;\n std::cout \u003c\u003c \"error == std::errc::permission_denied: \"\n \u003c\u003c (error == std::errc::permission_denied ? \"YES\" : \"NO\") \u003c\u003c std::endl;\n \n // Cleanup\n chmod(\"root/middle\", 0755);\n std::filesystem::remove_all(\"root\");\n \n return error.value() == EEXIST ? 1 : 0; // Return 1 if bug is present\n}\n```\n\n### Compile and Run\n\n**On AIX (as non-root user):**\n```bash\n/opt/clang+llvm-20.1.7-powerpc64-ibm-aix-7.2/bin/clang++ -o test-clang-fs-removeall-bug.out test-clang-fs-removeall-bug.cpp\n./test-clang-fs-removeall-bug.out\n```\n\n**On Linux (as non-root user):**\n```bash\nclang++ -o test-clang-fs-removeall-bug.out test-clang-fs-removeall-bug.cpp\n./test-clang-fs-removeall-bug.out\n```\n\n**Note**: Must run as non-root user to trigger the permission error.\n\n### Output on Linux (clang 20.1.8 - Correct Behavior)\n```\nEACCES = 13\nEEXIST = 17\nerror.value() = 13\nerror.message() = \"Permission denied\"\nerror == std::errc::permission_denied: YES\n```\n\n### Output on AIX (clang 20.1.7 - Bug Present)\n```\nEACCES = 13\nEEXIST = 17\nerror.value() = 17\nerror.message() = \"File exists\"\nerror == std::errc::permission_denied: NO\n```\n\n**Verification**: This bug has been confirmed across multiple compilers and platforms:\n- **AIX with clang 20.1.7 + libc++**: Bug present (returns EEXIST instead of EACCES)\n- **Linux with clang 20.1.8 + libc++**: Bug NOT present (returns EACCES correctly)\n- **AIX with GCC 12.3.0 + libstdc++**: Bug NOT present (returns EACCES correctly)\n\nThis confirms the bug is **specific to clang's libc++ implementation on AIX**, not a general AIX filesystem issue or a general clang/libc++ bug. The same AIX system works correctly with GCC's libstdc++.\n\n## Root Cause Analysis\nThe bug appears to be in the AIX-specific implementation of `std::filesystem::remove_all()` in libc++. When the underlying system call (likely `rmdir()` or `unlink()`) fails with `errno = 13` (EACCES), the error is being incorrectly translated to `std::error_code` with value 17 (EEXIST).\n\nThis issue only occurs when running as a **non-root user** because:\n1. Root users bypass permission checks, so no EACCES error occurs\n2. Non-root users encounter permission errors when trying to remove read-only directories\n3. The bug in error code translation only manifests when EACCES is actually returned by the system\n\n## Workaround\n\n@richardlau created a patch in Node.js to workaround this. \n\nref: https://github.com/nodejs/node/pull/62788\n\nCC \n\n@nodejs/platform-aix \n\nWe need to file this report to AIX compilers team too.","author":{"url":"https://github.com/abmusse","@type":"Person","name":"abmusse"},"datePublished":"2026-04-17T15:21:48.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":1},"url":"https://github.com/62790/node/issues/62790"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:b71050ad-3e1a-08c9-76ce-5a5ccef7b58d |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | B92C:27C67C:16856A0:2079E99:6A4CEC09 |
| html-safe-nonce | b50714002941dec86b7af16eedc0b22722c3f3b300956d6f4a864415ce83ef1e |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJCOTJDOjI3QzY3QzoxNjg1NkEwOjIwNzlFOTk6NkE0Q0VDMDkiLCJ2aXNpdG9yX2lkIjoiMzczNTQ1NDQ1MjA3MDI4MDIwMSIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | 1d0b0cab041e9f5bf4aa323e8fc79525985d6b992d5d8314c2d1b9f9ed70a21b |
| hovercard-subject-tag | issue:4283634739 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/nodejs/node/62790/issue_layout |
| twitter:image | https://opengraph.githubassets.com/10186d24306b626a63d9f3ec8973db187d95c25a468695e3efe5e9c1243c663e/nodejs/node/issues/62790 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/10186d24306b626a63d9f3ec8973db187d95c25a468695e3efe5e9c1243c663e/nodejs/node/issues/62790 |
| og:image:alt | Summary On AIX with clang/libc++, std::filesystem::remove_all() incorrectly returns error code 17 (EEXIST - "File exists") instead of 13 (EACCES - "Permission denied") when encountering permission ... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | abmusse |
| hostname | github.com |
| expected-hostname | github.com |
| None | 299b43bca6e02ad35197ffeba30d2466846d5fb02ab96fbced5b5e6cec589fb8 |
| turbo-cache-control | no-preview |
| go-import | github.com/nodejs/node git https://github.com/nodejs/node.git |
| octolytics-dimension-user_id | 9950313 |
| octolytics-dimension-user_login | nodejs |
| octolytics-dimension-repository_id | 27193779 |
| octolytics-dimension-repository_nwo | nodejs/node |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 27193779 |
| octolytics-dimension-repository_network_root_nwo | nodejs/node |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | c5a57f04eeb310f57c73fd6d751d957e2ca27ed2 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width