When using data/mutation libraries, treat error handling as a two-step process: (1) type errors conservatively at the boundary, and (2) implement recovery/UI logic that matches the library’s actual error-state semantics.

1) Use conservative global error typing

2) Don’t assume placeholder/optimistic flows preserve error status

3) Wire recovery callbacks to the correct inputs

4) Place try/catch only where the promise can throw

Example (TanStack Query placeholder + conservative error typing):

import { useQuery } from '@tanstack/react-query'

useQuery({
  queryKey: ['some-query'],
  queryFn: async () => {
    // ensure you narrow before using error details
    if (Math.random() > 0.5) throw new Error('some error')
    return 42
  },
  // placeholderData replaces keepPreviousData
  placeholderData: (previous) => previous,
})

// At call sites with global error = unknown, narrow explicitly before using properties.
// Example:
// if (error instanceof SomeError) { error.code ... }

Follow these rules to prevent accidental “error access” without narrowing, avoid UI bugs caused by changed status semantics, and ensure rollback and error handling occur in the right place.