Empty catch {} blocks hide real failures and make debugging impossible; several reviews found bugs that existed only because a failure path logged nothing. Treat every catch as a decision point, not a place to discard information:
error.message) so a future failure is diagnosable, unless the condition is a genuinely expected race that is explicitly documented as such.logger.warn to every catch produces noise for ordinary races (e.g. ENOENT because another process already removed a lock/file). Check for the known benign error code first and return/continue quietly; reserve warn/error logs for failures that actually need attention:
try {
await fs.rm(lockDir, { recursive: true, force: true });
} catch (error) {
if (isEnoent(error)) return; // another holder already cleaned it up
logger.warn("failed to release lock dir", { lockDir, error: String(error) });
}
const reportError = (error: Error): void => {
if (reported) return;
reported = true;
try {
onError?.(error);
} catch (hookError) {
logger.warn("error handler itself threw", { error: String(hookError) });
}
};
try {
await op();
} catch (error) {
reportError(toError(error)); // original error is still the one reported
}
Applying these rules turns a swallowed failure into either a traceable log line or a correctly-propagated error, and prevents unrelated secondary failures from hiding the root cause.