Error Handling
Error boundaries, toast notifications, Edge Function error patterns, and logging strategies.
Overview
ScaleRocket handles errors at multiple layers: React error boundaries for crashes, toast notifications for user-facing feedback, structured error handling in Edge Functions, and environment-aware logging.
Error Boundaries
error.tsx
Next.js automatically catches rendering errors when you create an error.tsx file:
// src/app/error.tsx
"use client";
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<div className="flex min-h-screen flex-col items-center justify-center">
<h2 className="text-2xl font-bold">Something went wrong</h2>
<p className="mt-2 text-gray-500">{error.message}</p>
<button onClick={reset} className="mt-4 rounded bg-indigo-600 px-4 py-2 text-white">
Try again
</button>
</div>
);
}The reset function re-renders the segment, allowing recovery without a full page reload.
not-found.tsx
Handle 404 errors with a custom page:
// src/app/not-found.tsx
export default function NotFound() {
return (
<div className="flex min-h-screen flex-col items-center justify-center">
<h2 className="text-4xl font-bold">404</h2>
<p className="mt-2 text-gray-500">Page not found.</p>
<a href="/" className="mt-4 text-indigo-600 hover:underline">
Go home
</a>
</div>
);
}Toast Notifications
For non-fatal errors (failed API calls, validation errors), use the toast system instead of error boundaries:
import { useToast } from "@saas/ui";
const { toast } = useToast();
try {
await updateProfile(data);
toast({ title: "Profile updated" });
} catch (err) {
toast({
title: "Update failed",
description: err instanceof Error ? err.message : "Please try again.",
variant: "destructive",
});
}This keeps the user on the current page while showing feedback.
Edge Function Error Patterns
Structure Edge Function responses with consistent error shapes:
// supabase/functions/my-function/index.ts
try {
const result = await processRequest(req);
return new Response(JSON.stringify({ data: result }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
} catch (error) {
return new Response(
JSON.stringify({ error: error.message || "Internal error" }),
{ status: 500, headers: { "Content-Type": "application/json" } }
);
}Common Supabase Error Codes
| Code | Meaning | Handling |
|---|---|---|
23505 | Unique constraint violation | Show "already exists" message |
42501 | RLS policy violation | Show "not authorized" message |
PGRST116 | Row not found | Show 404 or "not found" message |
23503 | Foreign key violation | Show "related record required" message |
Logging Strategy
Development
Log errors with full stack traces for debugging:
if (process.env.NODE_ENV === "development") {
console.error("Full error:", error);
console.error("Stack:", error.stack);
}Production
In production, avoid leaking internal details to the console. Use structured logging or send errors to a monitoring service:
if (process.env.NODE_ENV === "production") {
// Send to error tracking service (Sentry, LogRocket, etc.)
captureException(error);
}Never expose raw database error messages, stack traces, or internal paths to end users.
Best Practices
- Use error boundaries for unexpected crashes; use toasts for expected failures.
- Always provide a recovery action (retry button, link to home).
- Validate inputs before making API calls to reduce error frequency.
- Return user-friendly messages; log technical details server-side.
Done reading? Mark this page as complete.