nodejs / nodejs/node

fs: rmSync() reports UV_UNKNOWN with an empty code for unmapped errno values

Đang mở
#65,884 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

Ngôn ngữ chính
JavaScript
Star
122k
Fork
37.4k
Merge trung bình
4 ngày 3 giờ
Pull request đã merge (30 ngày)
272

Mô tả

Version

v26.7.0

Platform

Linux x64

Subsystem

fs / src/node_file.cc

What steps will reproduce the bug?
const fs = require('node:fs');
fs.mkdirSync('a/b', { recursive: true });
try {
  fs.rmSync('a/.', { recursive: true, force: true });
} catch (err) {
  console.log('code   :', JSON.stringify(err.code));
  console.log('errno  :', err.errno);
  console.log('syscall:', err.syscall);
  console.log('message:', err.message);
}

Output:

code   : ""
errno  : -4094
syscall: "rm"
message: , Unknown error: Invalid argument 'a/.'
What is the expected behavior? Why is that the expected behavior?

err.code should be 'EINVAL' and err.errno should be -22. The underlying
failure is a plain EINVAL from rmdir(2), which POSIX requires for a path whose
final component is ..

Every other fs error carries a usable code, and err.code === 'EINVAL' is the
documented way to branch on fs failures, so an empty string breaks ordinary error
handling. The message is also malformed: it begins with , because the empty code
is interpolated into it.

What do you see instead?

errno is -4094 (UV_UNKNOWN) and code is the empty string.

Additional information

This is not specific to EINVAL. RmSync() in src/node_file.cc translates the
std::error_code from std::filesystem::remove_all() with a hardcoded chain that
covers four values:

  • operation_not_permitted -> EPERM
  • directory_not_empty -> ENOTEMPTY
  • not_a_directory -> ENOTDIR
  • permission_denied -> EACCES (EPERM on Windows)

Anything else falls through to the tail of the function:

std::string message = "Unknown error: " + error.message();
return env->ThrowErrnoException(
    UV_UNKNOWN, "rm", message.c_str(), path_c_str);

So the mapping is an allowlist rather than a translation, and any errno outside
those four surfaces as UV_UNKNOWN with an empty code. EINVAL is simply the
one that is easiest to trigger from user code.

The async form is unaffected, because fs.rm() and fsPromises.rm() use the JS
rimraf and report EINVAL correctly.

Noting for context that the dot segment handling in #61958 is being addressed
separately at the JS layer. That change stops a/. from reaching this code path,
but it does not fix the mapping, which stays reachable for other errnos.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Hướng nghiên cứu

Đọc src/node_file.cc tại RmSync() và theo dõi cách std::error_code từ std::filesystem::remove_all() trở thành lỗi Node.js. Thêm kiểm thử hồi quy cho một errno chưa được ánh xạ như EINVAL, xác minh rằng code, errno và message được trả về có thể sử dụng được thay vì là UV_UNKNOWN với code rỗng.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
cpp, javascript, nodejs
Lĩnh vực
operating-systems
Loại issue
Lỗi
Độ khó
3/5
Thời gian dự kiến
1-2 ngày
Mức độ hoạt động
Sôi nổi
Độ rõ ràng
Đặc tả rõ ràng
Mức phù hợp với người mới
76/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.