Error Handling — Frontend
| Field | Details |
|---|---|
| Status | Active |
| Last Updated | 05-12-2026 |
Purpose
Consistent, user-friendly error handling across all frontend apps.
Scope
Applies to: All frontend applications Does not apply to: Backend services, third-party libraries
Rules
- Never silently catch errors — log them or show feedback
- Always show a human-readable message — never raw errors or stack traces
- Handle API errors through a shared wrapper
- Use error codes from Error Codes — never invent new ones in UI
Async / Try-Catch — always handle, never hang
Every async call must have a try/catch. Don't let a promise fail silently.
async function submitInvoice(data: InvoiceData) {
try {
setLoading(true)
setError(null)
await api.createInvoice(data)
} catch (err) {
setError(toUserMessage(err))
} finally {
setLoading(false)
}
}
Nested async calls
When one async function calls another, don't wrap every level in try/catch. Let the inner function throw, and catch it once at the top (usually where the UI lives).
// Inner — just throws, no try/catch
async function fetchInvoice(id: string) {
const res = await fetch(`/api/invoices/${id}`)
return handleApiResponse<Invoice>(res) // throws on failure
}
// Middle — also just throws
async function loadInvoiceWithItems(id: string) {
const invoice = await fetchInvoice(id)
const items = await fetchItems(invoice.id)
return { invoice, items }
}
// Top (UI) — the ONLY place that catches
async function onOpenInvoice(id: string) {
try {
setLoading(true)
const data = await loadInvoiceWithItems(id)
setInvoice(data)
} catch (err) {
setError(toUserMessage(err))
} finally {
setLoading(false)
}
}
Rule of thumb: catch where you can do something about it (show a message, retry, redirect). Everywhere else, just await and let it bubble up.
If you start a promise without awaiting it (fire-and-forget), always attach .catch:
trackEvent("invoice_opened").catch((err) => logger.error("tracking failed", err))
Form Validation — field-level only
Show the specific field issue. Never show a generic "something went wrong" for validation.
{errors.email && <span className="field-error">{errors.email}</span>}
Exceptions
None. Every interaction must handle failure gracefully.
Related Documents
Changelog
| Version | Date | Author | Change |
|---|---|---|---|
| 1.0.0 | 05-12-2026 | Tibin Sunny | Initial version |