Adopt a small set of guardrails for failures so recovery is predictable, repeatable, and doesn’t deadlock or mask outcomes:
1) Classify errors into “retryable” vs “permanent,” then apply a bounded retry budget.
2) Validate recovery inputs to prevent “false live” state or unexpected argument errors.
3) Never let cleanup/recovery throw in a way that overwrites the primary outcome.
4) Ensure prerequisites are materialized before running dependent steps.
Example (retry budget + reset + permanent quarantine):
const PERMANENT_OPEN_CODES = new Set([
"EACCES","EPERM","EISDIR","ELOOP","ENAMETOOLONG","ENOTDIR",
]);
const MAX_SESSION_OPEN_ATTEMPTS = 5;
async function readSessionUsage({ session, open, path }) {
try {
const file = await open(path, "r");
session.openFailures = 0; // reset on any success
// ...read usage...
} catch (err: any) {
if (PERMANENT_OPEN_CODES.has(err?.code)) {
quarantineSession(session);
return; // or return null, depending on API
}
session.openFailures = (session.openFailures ?? 0) + 1;
if (session.openFailures >= MAX_SESSION_OPEN_ATTEMPTS) {
quarantineSession(session);
}
throw err;
}
}
Use these rules consistently across scanning, lock recovery, resume flows, and filesystem staging so failures degrade gracefully, don’t mask the true outcome, and don’t repeatedly re-trigger the same bad state.