When catching errors from syscalls, spawned processes, or optional APIs, never collapse distinct failure modes into one generic catch. Check the specific error code/condition and handle only the exact case you mean to special-case; let anything else either propagate or be reported with its own precise status, instead of silently mapping it onto an unrelated outcome.
Concretely:
ENOENT/ENOTDIR for “missing”) rather than catching broadly and assuming one meaning. A permission error (EACCES) or I/O error on a stat() is not the same as “doesn’t exist” and should surface as its own failure/reason (e.g. “inaccessible”) rather than being treated as “not present”.ENOENT, ENOEXEC, EACCES) and treat only those as the expected condition; any other error should propagate so it isn’t masked.ENOENT) and treat it as a no-op, not as a hard error.Example pattern:
try {
await fs.promises.stat(path);
} catch (error) {
if (error?.code === "ENOENT" || error?.code === "ENOTDIR") {
return { status: "missing" };
}
// Don't swallow EACCES/EIO/etc. as "missing" — report distinctly.
return { status: "inaccessible", error };
}
Applying this discipline prevents misleading outcomes (e.g. reporting “no hook” when a hook exists but is unreadable, or crashing on an unrelated race) and makes error messages and recovery behavior accurate and actionable.