Adopt a single standard for error handling in async/control-flow code:
1) Never let setup/teardown exceptions escape their owner
promise.then(fn, onError) when fn can throw; prefer promise.then(fn).catch(fail).2) Prevent unhandled rejections from “cleanup” operations
fail/record mechanism.3) Enforce lifecycle invariants explicitly (no “rare null” fallthrough)
4) Avoid silent fail-open on configuration/settings reads
5) Make retry/recovery semantics-driven
6) Best-effort telemetry must not interrupt the primary flow
Example pattern
function runWithFail<T>(p: Promise<T>, fail: (err: unknown) => void): void {
void p.then(() => {}).catch(fail);
}
// 1) Catch exceptions thrown inside attach
const handle = createHandle();
void loadSandbox()
.then((mxc) => mxc.spawn(config))
.then((sandboxed) => {
// attach may throw (stream setup, environment checks, etc.)
handle.attach(sandboxed);
})
.catch((err) => {
handle.fail(err instanceof Error ? err : new Error(String(err)));
});
// 2) No unhandled kill/release rejections
async function safeKill(proc: { kill(): void; dispose(): void }) {
try {
proc.kill();
} catch (e) {
// Route through the same error reporting path, don't throw/reject uncaught.
report(e);
} finally {
proc.dispose();
}
}
// 3) No rare-state fallthrough
if (liveSession && !liveSessionCanStartRun) {
await ensureSessionPersisted(liveSession);
if (!liveSession.artifacts) {
// Invariant violated: do not fall through to replacement path.
throw new Error('Live session missing persisted artifacts; refusing to replace');
}
}
Adopting these rules prevents hangs/unhandled rejections, preserves session/task safety, and ensures retry/recovery/telemetry behave according to their real semantics rather than incidental error shape.