You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Node reports hard-link syscall failures through fs.linkSync, callback fs.link, and fs.promises.link. Perry's current link helper reduces std::fs::hard_link failures to 0, while callback and promise wrappers ignore that status. Failed hard-link creation can therefore look like a successful no-op.
Common cases include missing source files, missing destination parents, and existing destination paths.
Node behavior
Local Node probe on v25.9.0:
constfs=require("node:fs");constfsp=require("node:fs/promises");fs.linkSync("missing-src.txt","dest.txt");// throws Error ENOENT, syscall "link", path oldPath, dest newPathfs.linkSync("src.txt","missing-parent/dest.txt");// throws Error ENOENT, syscall "link", path oldPath, dest newPathfs.linkSync("src.txt","existing.txt");// throws Error EEXIST, syscall "link", path oldPath, dest newPathfs.link("src.txt","existing.txt",(err)=>{console.log(err.code,err.syscall,err.path,err.dest);});awaitfsp.link("src.txt","existing.txt");// rejects with the same EEXIST / link / path / dest shape
Observed shape:
missing source sync: Error ENOENT syscall=link path=<old> dest=<new>missing destination parent sync: Error ENOENT syscall=link path=<old> dest=<new>existing destination sync: Error EEXIST syscall=link path=<old> dest=<new>callback and promises report/reject with the same fields
Perry evidence from origin/main
crates/perry-runtime/src/fs/mod.rs::js_fs_link_sync() returns only a success/failure sentinel:
if fs::hard_link(from, to).is_ok(){1}else{0}
crates/perry-runtime/src/fs/callbacks.rs::js_fs_link_callback() preflights only the source path, then ignores the actual hard-link result and always calls success:
Summary
Node reports hard-link syscall failures through
fs.linkSync, callbackfs.link, andfs.promises.link. Perry's currentlinkhelper reducesstd::fs::hard_linkfailures to0, while callback and promise wrappers ignore that status. Failed hard-link creation can therefore look like a successful no-op.Common cases include missing source files, missing destination parents, and existing destination paths.
Node behavior
Local Node probe on v25.9.0:
Observed shape:
Perry evidence from
origin/maincrates/perry-runtime/src/fs/mod.rs::js_fs_link_sync()returns only a success/failure sentinel:crates/perry-runtime/src/fs/callbacks.rs::js_fs_link_callback()preflights only the source path, then ignores the actual hard-link result and always calls success:That misses destination-side failures such as
EEXISTand missing destination parents.crates/perry-runtime/src/node_submodules/fs_promises.rs::thunk_fs_promises_link()also discards the status and always resolves:The sync path uses the same status-returning helper, so syscall failures have no typed error value to throw.
Expected compatibility
fs.linkSync(existingPath, newPath)should throw a Node-shaped fs error when hard-link creation fails.fs.link(existingPath, newPath, cb)should pass that error to the callback.fs.promises.link(existingPath, newPath)should reject with that error.code,syscall: "link",path, anddest.Suggested test surface
Add deterministic parity tests for:
undefined.Scope / non-goals
link/linkSync/fs.promises.link.readlink(node:fs: report readlink errors instead of empty targets #2733),rename(node:fs: propagate rename errors across sync, callback, and promise APIs #2735),unlink(node:fs: propagate unlink errors across sync, callback, and promise APIs #2736), orcopyFile(node:fs: propagate copyFile errors across sync, callback, and promise APIs #2737).code,syscall,path, anddestfields.Duplicate check
Searched issues and PRs for:
fs link EEXIST ENOENTfs.promises.linklinkSync hard linkfs link errorBroad
fs link errorsearch only returned unrelated/noisy hits and other fs issues; no hard-link error-propagation issue or PR appeared.