Failure-Safe State Handling

When error handling affects control-flow invariants (dedupe keys, stale-session guards, correctness checks), make failures safe and reversible: - Cleanup on every failure path: If you “remember”/lock/dedupe before completing the operation, ensure that early returns and catch blocks restore the pre-operation state (e.g., remove the remembered key).

copy reviewer prompt

Prompt

Reviewer Prompt

When error handling affects control-flow invariants (dedupe keys, stale-session guards, correctness checks), make failures safe and reversible:

  • Cleanup on every failure path: If you “remember”/lock/dedupe before completing the operation, ensure that early returns and catch blocks restore the pre-operation state (e.g., remove the remembered key).
  • Don’t bypass correctness in catch: If a guard depends on remote/local state fetch, handle fetch errors explicitly. Don’t fall back to “proceed anyway” in a way that permits stale/out-of-date actions.
  • Test recovery behavior: Add a retry regression test covering the failure scenario so the second attempt behaves correctly.

Example pattern (dedupe cleanup):

const dedupeKey = imageBlobDedupeKey(blob, data)
if (rememberRecentImageBlobPaste(refs, dedupeKey)) return
try {
  const ok = await saveImageBuffer(...)
  if (!ok) throw new Error('save failed')
} catch (e) {
  // restore invariant so immediate retry can attach
  forgetRecentImageBlobPaste(refs, dedupeKey)
  throw e
}

Example pattern (guard integrity):

try {
  const remote = await getSessionMessages(guardStoredId /* required routing */)
  // decide based on remote
} catch (e) {
  // safe default: block + refresh rather than submit with stale context
  await refreshSessionContext()
  return
}

Source discussions