# Error recovery does not lose user data

- **Pattern:** `ab-002304` (`saas-error-handling.graceful-degradation.error-recovery-no-data-loss`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002304
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002304)

## Why it matters

A user who fills out a multi-field signup form, hits a server error, and finds all their input cleared will abandon rather than re-type. ISO 25010 reliability.fault-tolerance requires that fault recovery not introduce secondary failures — clearing user data on error is exactly that. This is especially damaging in multi-step onboarding flows, payment forms, and file upload workflows where re-entering data has high friction. Catch blocks that call `form.reset()` or `setState({})` on error are the most common defect pattern: the form correctly shows an error message but simultaneously destroys the data the user needs to re-submit.

## Severity rationale

High because error handlers that clear form state force users to re-enter all input after a server failure, causing direct abandonment in high-value conversion flows.

## Remediation

Ensure catch blocks never reset form state — only update the error message and re-enable the submit button.

```ts
// Wrong — destroys user input on error
} catch (error) {
  form.reset()
  setError('Submission failed')
}

// Right — preserves user input, only surfaces the error
} catch (error) {
  setSubmitError('Submission failed. Your data is preserved — please try again.')
  setIsSubmitting(false)
}
```

For multi-step flows, store step state in a parent component or context that persists across step unmounts rather than inside individual step components. For file uploads, keep the `File` object in parent state and only clear it after confirmed server-side success — never on upload failure.

## Detection

- **ID:** `error-recovery-no-data-loss`
- **Severity:** `high`
- **What to look for:** Focus on forms and multi-step workflows. When an error occurs (network failure, server error, validation rejection): (1) Does the form preserve the user's input in state, or does the error state reset the form fields? (2) Do multi-step wizards or onboarding flows preserve progress across errors? (3) Do "Try again" buttons re-submit with the same data, or do they clear state and require the user to re-enter everything? Look for patterns where `catch` blocks call `resetForm()` or `setState({})` — these likely clear user data on error. Also check whether long-form inputs (rich text editors, file uploads) survive error conditions.
- **Pass criteria:** Count all forms and multi-step flows in the project and test each for data preservation on error. Pass if form state is preserved on error (input fields retain their values after a failed submission), and retry mechanisms re-submit with the preserved data. At least 90% of forms must preserve state on error. Report the ratio: "X of Y forms preserve user input on error."
- **Fail criteria:** Fail if form fields are cleared on error (user must re-type their input). Fail if error boundaries' `reset()` function causes parent state to reset, clearing form data. Fail if multi-step form progress is lost on any individual step error.
- **Skip (N/A) when:** The application has no forms and no multi-step workflows. Signal: no `<form>` elements, no form library, no wizard or stepper components.
- **Detail on fail:** Describe the data loss scenario (e.g., "Signup form's catch block calls form.reset() on API error — user loses all typed input; file attachment is cleared from state on upload failure"). Max 500 chars.
- **Remediation:** Losing user data on error is the most frustrating failure mode. Users who have to re-enter a long form after a server error often abandon entirely.

  Ensure error handling preserves form state:

  ```ts
  // Wrong — clears user data on error
  } catch (error) {
    form.reset()
    setError('Submission failed')
  }

  // Right — preserves user data, only sets error state
  } catch (error) {
    setSubmitError('Submission failed. Your data is preserved — please try again.')
    setIsSubmitting(false)
  }
  ```

  For multi-step flows, store state in a parent component or context that persists across step re-renders, not in individual step components that unmount on error.

  For file uploads, store the File object reference in state so users don't need to re-select after a failed upload:

  ```ts
  // Keep the File in state; only clear after confirmed server-side success
  const [selectedFile, setSelectedFile] = useState<File | null>(null)
  ```

---

## External references

- iso-25010 reliability.fault-tolerance

Taxons: user-experience, error-resilience

HTML version: https://auditbuffet.com/patterns/ab-002304
