Error state variables that are set on failure but never cleared produce a user-hostile artifact: a stale error banner that survives successful navigations, retries, and even cross-session logins on shared devices. Users who have already resolved the issue still see the old message, which undermines trust in the UI's accuracy and can leak information about a previous session's activity on a shared browser. Clearing error state before retries also prevents the double-state flash where old and new errors render simultaneously.
Low because the bug is cosmetic and self-limiting, but it visibly erodes trust every time it appears.
Reset every error state variable before the action that might produce a new one, and expose a dismiss mechanism on persistent banners. For global stores, add a clearError action and call it on route changes. Audit files under src/hooks/ and src/stores/ for setError calls without matching resets.
async function handleRetry() {
setError(null)
setLoading(true)
try { await fetchData() }
catch (err) { setError('Still failing.') }
finally { setLoading(false) }
}
For Zustand or Redux stores add clearError: () => set({ error: null }) and invoke it on navigation.
ID: saas-error-handling.graceful-degradation.error-state-reset-mechanism
Severity: low
What to look for: Look at error state management throughout the application. Examine: (1) When an error state is set (e.g., setError('Failed to load')), is there a mechanism to clear that state — either automatically on the next successful action, or via a user dismiss/close action? (2) Do error banners/toasts auto-dismiss, or do they persist indefinitely? (3) When a user retries an action that previously failed, is the previous error message cleared before the retry? (4) Do modal dialogs with error states allow dismissal? Look for error state variables that are set on failure but never cleared to null.
Pass criteria: Count all error state variables (useState for errors) in the project. Pass if error states are cleared either automatically (on successful retry, on user navigation, or after a timeout) or via an explicit dismiss mechanism (close button, "Try again" button that resets error state before re-submitting). At least 90% of error states must have a reset mechanism. Report even on pass: "X of Y error state variables have reset mechanisms."
Fail criteria: Fail if error messages persist indefinitely with no way to dismiss them. Fail if retrying an action shows both the previous error message and the new loading/error state simultaneously. Fail if error state variables are set in multiple places but never reset.
Skip (N/A) when: The project uses React Query or SWR for all data fetching (these libraries manage error state lifecycle automatically) and has no manual error state variables. Evaluate by checking for useState for error management alongside these libraries.
Detail on fail: "Error state in useAuthStore is set on login failure but never cleared on successful navigation; logging in on a different device shows the previous session's error". Max 500 chars.
Remediation: Persistent error states confuse users who have already resolved the issue, or who encounter a stale error from a previous session.
Clear error state before retry actions:
async function handleRetry() {
setError(null) // always clear before retry
setLoading(true)
try {
await fetchData()
} catch (err) {
setError('Still failing. Please try again later.')
} finally {
setLoading(false)
}
}
For global error state in a store (Zustand, Redux), add a reset action:
clearError: () => set({ error: null }),
Call clearError() on route changes and at the start of any action that might have previously failed.