nodejs / nodejs/node

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

Aperta
#65,884 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Lingua principale
JavaScript
Stelle
122k
Fork
37.3k
Merge medio
4g 2h
PR unite (30g)
283

Descrizione

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.

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Leggi src/node_file.cc in RmSync() e traccia come lo std::error_code di std::filesystem::remove_all() diventa un errore Node.js. Aggiungi una copertura di regressione per un errno non mappato come EINVAL, verificando che il codice, errno e messaggio restituiti siano utilizzabili invece di essere UV_UNKNOWN con un codice vuoto.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
cpp, javascript, nodejs
Ambito
operating-systems
Tipo di issue
Bug
Difficoltà
3/5
Tempo stimato
1-2 giorni
Stato di attività
Attiva
Chiarezza
Specificata chiaramente
Idoneità per principianti
76/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.