nodejs / nodejs/node

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

Abierto
#65,884 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Lenguaje dominante
JavaScript
Estrellas
122k
Forks
37.4k
Merge medio
4 d 3 h
PR fusionados (30 d)
272

Descripción

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.

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Lee src/node_file.cc en RmSync() y rastrea cómo el std::error_code de std::filesystem::remove_all() se convierte en un error de Node.js. Añade cobertura de regresión para un errno no asignado como EINVAL, verificando que el código, errno y mensaje devueltos sean utilizables en lugar de ser UV_UNKNOWN con un código vacío.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
cpp, javascript, nodejs
Área
operating-systems
Tipo de issue
Error
Dificultad
3/5
Tiempo estimado
1-2 días
Estado de actividad
Activo
Claridad
Bien especificado
Aptitud para principiantes
76/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.