Implement proper error handling
Use try-catch blocks and error boundaries to gracefully handle errors in async operations and UI components.
- Wrap async operations in try-catch blocks
- Use Error Boundaries in React for UI-level error handling
- Log errors with context for debugging
- Always re-throw or handle—never silently swallow errors
Rule Details
Proper error handling prevents crashes, improves debugging, and creates better user experiences.
Code Examples
Basic Pattern
// ❌ No error handling
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`)
return response.json()
}
// ✅ With error handling
async function fetchUser(id) {
try {
const response = await fetch(`/api/users/${id}`)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
return await response.json()
} catch (error) {
console.error('Failed to fetch user:', error)
throw error // Re-throw for caller to handle
}
}With Error Recovery
async function fetchUserWithFallback(id) {
try {
const response = await fetch(`/api/users/${id}`)
return await response.json()
} catch (error) {
console.error('API failed, using cache:', error)
// Fallback to cached data
const cached = localStorage.getItem(`user_${id}`)
if (cached) {
return JSON.parse(cached)
}
// If no cache, show user-friendly error
throw new Error('Unable to load user data. Please try again.')
}
}Why It Matters
Unhandled errors crash applications, create poor user experiences, and make debugging impossible without proper context.
Promise Error Handling
// ❌ Unhandled promise rejection
fetch('/api/data')
.then(response => response.json())
.then(data => processData(data))
// ✅ With error handling
fetch('/api/data')
.then(response => {
if (!response.ok) throw new Error('Network response was not ok')
return response.json()
})
.then(data => processData(data))
.catch(error => {
console.error('Fetch failed:', error)
showErrorMessage('Failed to load data')
})React Error Boundaries
import { Component, ErrorInfo, ReactNode } from 'react'
interface Props {
children: ReactNode
fallback?: ReactNode
}
interface State {
hasError: boolean
error?: Error
}
class ErrorBoundary extends Component<Props, State> {
state: State = { hasError: false }
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error }
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('Error caught by boundary:', error, errorInfo)
// Report to error tracking service
reportError(error, errorInfo)
}
render() {
if (this.state.hasError) {
return this.props.fallback || (
<div role="alert">
<h2>Something went wrong</h2>
<button onClick={() => this.setState({ hasError: false })}>
Try again
</button>
</div>
)
}
return this.props.children
}
}
// Usage
function App() {
return (
<ErrorBoundary fallback={<ErrorPage />}>
<MyComponent />
</ErrorBoundary>
)
}Global Error Handling
// Catch unhandled promise rejections
window.addEventListener('unhandledrejection', (event) => {
console.error('Unhandled promise rejection:', event.reason)
// Report to error tracking
reportError(event.reason)
// Optionally prevent default browser behavior
event.preventDefault()
})
// Catch global errors
window.addEventListener('error', (event) => {
console.error('Global error:', event.error)
reportError(event.error)
})Best Practices
- Always handle async errors - Use try-catch or .catch()
- Don't swallow errors - Log and report them
- Provide user feedback - Show meaningful error messages
- Implement recovery - Offer retry or fallback options
- Use error boundaries - Prevent entire app crashes
- Track errors - Use services like Sentry or LogRocket
Standards
- Use MDN: JavaScript Guide as the standard for how this JavaScript pattern should behave in production, not just in a small local example.
- Use web.dev: Learn JavaScript as the standard for how this JavaScript pattern should behave in production, not just in a small local example.
Verification
- Verify the behavior in the browser after the code change, not only in static analysis.
- Inspect DevTools Network or Performance panels when the rule affects loading or execution order.
- Test the primary user flow and one edge case triggered by the changed script path.
- Confirm the code still behaves correctly when the feature is delayed, lazy-loaded, or fails.
Use with AI
Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.
Check
Verify implementation
Review error handling in this code, ensuring async operations use try-catch and errors are properly logged.
Fix
Auto-fix issues
Add try-catch blocks to async operations and implement error boundaries for React components.
Explain
Learn more
Explain JavaScript error handling best practices including try-catch, Promise rejection, and error boundaries.
Review
Code review
Review scripts, client components, and browser execution paths related to Implement proper error handling. Flag exact imports, event handlers, runtime side effects, or blocking operations that violate the rule, and state how the change should be verified in the browser.