DEV Community

Vishal Chauhan
Vishal Chauhan

Posted on

How to Know When Your Vibe-Coded App Breaks Before Your Users Do: A 7-Step Beginner Guide to Error Alerts

Your app is live. You tested signup, you tested the main feature, everything worked. Then a week later someone replies to your launch post: "Is your site broken? I can't log in."

Too often, that is how founders find out their app is broken: from a user, days late, after other people quietly gave up and left. AI tools like Lovable, Bolt, v0, Cursor and Claude Code are great at building the happy path. They usually do not set up the part that tells you when something fails in production.

This guide fixes that in an afternoon. By the end you will have three things: an error tracker that catches crashes and silent failures, an outside uptime check that notices when the app or database is down, and alerts that actually reach you. All of it starts on free plans.

Quick glossary

  • Error tracker: a service (this guide uses Sentry) that records every error your app throws in a real user's browser or on your server, groups repeats together, and shows you where it happened.
  • DSN: the address your app uses to send errors to your Sentry project. Sentry's docs say it is safe to keep public, because it only allows sending new events, not reading any data.
  • Uptime monitor: an outside robot that loads a URL every few minutes and alerts you when it fails or the page looks wrong.
  • Source map: a file that translates your minified production code back into your real file names and line numbers, so error reports are readable.

Step 1: Write down the 5 things that must never break

Before installing anything, decide what "broken" means for your app. You do not need to watch everything. Fill in this template:

MUST-NEVER-BREAK LIST
1. Signup (new account, new device, logged out)
2. Login and password reset
3. The one core action (the reason people use the app)
4. Saving data (create / edit / delete the main thing)
5. Payment and access after paying (if you charge)

For each one:
- Where does it run? (browser / API route / Edge Function / webhook)
- What does the user see if it fails? (error message / blank screen / nothing)
- How would I find out today? (honest answer)
Enter fullscreen mode Exit fullscreen mode

The last line is the important one. If the honest answer is "a user would tell me", that flow goes to the top of your list.

A hypothetical example: in a habit-tracker app, the core action is "mark today as done". If that save fails quietly, the user sees the checkmark, refreshes the next day, and finds their streak gone. Nothing crashed, so nobody gets told. That is exactly the kind of failure this guide is about.

Step 2: Add Sentry to your frontend

Create a free account at sentry.io, create a project, and pick your framework. Sentry's free Developer plan is for one person and includes error monitoring and email alerts. Check the pricing page for the current limits.

If your app is Next.js (common with v0, Cursor and Claude Code), run the official wizard in your project folder:

npx @sentry/wizard@latest -i nextjs
Enter fullscreen mode Exit fullscreen mode

It installs the SDK, creates the browser, server and edge config files, and wraps your next.config so source maps upload on build. It also asks whether to route browser requests through your own server to avoid ad blockers. Saying yes means fewer missed reports, with one trade-off from Sentry's docs: it can add a little server load and hosting cost.

If your app is React + Vite (common with Lovable and Bolt), install the SDK:

npm install @sentry/react
Enter fullscreen mode Exit fullscreen mode

Then create one small file and import it first:

// src/instrument.ts
import * as Sentry from "@sentry/react";

Sentry.init({
  dsn: import.meta.env.VITE_SENTRY_DSN,
  environment: import.meta.env.MODE, // "production" on your live build
  enabled: import.meta.env.PROD,     // ignore errors from your laptop
});
Enter fullscreen mode Exit fullscreen mode
// src/main.tsx - this import must be the FIRST line
import "./instrument";
Enter fullscreen mode Exit fullscreen mode

Put the DSN in your host's environment variables as VITE_SENTRY_DSN. A VITE_ variable ends up in the browser, which is fine for a DSN and never fine for secret keys.

Finally, wrap your app so a crash shows a friendly message instead of a white screen, and gets reported:

<Sentry.ErrorBoundary fallback={<p>Something went wrong. Please refresh.</p>}>
  <App />
</Sentry.ErrorBoundary>
Enter fullscreen mode Exit fullscreen mode

Test it properly. Add a temporary button that throws new Error("Sentry test"), deploy, click it on the live site, and check the Issues page. Sentry's docs warn that errors you trigger from the browser DevTools console are sandboxed and will not show up, so use a real button. Delete the button afterwards.

Step 3: Report the errors that do not crash anything

This is the step most guides skip, and it matters most for vibe-coded apps. Sentry automatically catches uncaught errors. But many real failures never throw:

  • Supabase queries return errors instead of throwing them. The official pattern is const { data, error } = await supabase.from(...).... If the code ignores error, the save fails silently and Sentry never hears about it.
  • fetch() does not fail on 404 or 500. MDN is clear: the promise only rejects on network-level failures. A 500 from your API looks like success unless you check response.ok.
  • AI-written try/catch blocks that only console.log the error. The log disappears in the user's browser.

Add one helper and use it everywhere something important can fail:

// src/lib/report.ts
import * as Sentry from "@sentry/react"; // or "@sentry/nextjs"

export function report(where: string, err: unknown) {
  const error =
    err instanceof Error ? err : new Error(JSON.stringify(err));
  Sentry.captureException(error, { tags: { where } });
}
Enter fullscreen mode Exit fullscreen mode

Then use it on your must-never-break flows:

const { error } = await supabase.from("habits").insert(newHabit);
if (error) {
  report("save-habit", error);
  showToast("Could not save. Please try again.");
  return;
}
Enter fullscreen mode Exit fullscreen mode
const res = await fetch("/api/generate", { method: "POST", body });
if (!res.ok) report("generate", new Error(`API returned ${res.status}`));
Enter fullscreen mode Exit fullscreen mode

The user gets a clear message instead of a fake success, and the where tag lets you filter Sentry by flow later.

You do not have to do this by hand. Paste this into your AI tool:

Find every Supabase query, fetch() call and try/catch block in this
project. For each one on these flows: [paste your must-never-break list],
make sure errors are (1) passed to the report() helper in
src/lib/report.ts with a short "where" tag, and (2) shown to the user
as a clear message. Never show success if the operation failed.
Do not change any other behavior. List every file you changed.
Enter fullscreen mode Exit fullscreen mode

Step 4: Cover the backend too

Errors in your server code never reach the browser, so the frontend setup will not see them.

  • Next.js: the wizard from Step 2 already covers API routes, server components and middleware through its instrumentation.ts file. Nothing extra to do.
  • Supabase Edge Functions (where Lovable puts most backend logic): Supabase's own docs show adding the Sentry Deno SDK. The short version is to wrap your handler in try/catch, capture the error, and flush before returning:
import * as Sentry from "npm:@sentry/deno@^8";

Sentry.init({ dsn: Deno.env.get("SENTRY_DSN"), defaultIntegrations: false });

// inside your handler:
try {
  // ...your existing code...
} catch (e) {
  Sentry.captureException(e);
  await Sentry.flush(2000); // send before the function shuts down
  return Response.json({ error: "Internal error" }, { status: 500 });
}
Enter fullscreen mode Exit fullscreen mode

Add SENTRY_DSN under Edge Function Secrets in the Supabase dashboard. Sentry recommends one project per part of your app, so create a second Sentry project for the backend and use its DSN here. That way frontend and backend errors do not mix.

Do webhooks (Stripe, auth hooks) first. When those fail, no user is on screen to complain; the work just never happens.

Step 5: Turn on alerts that actually reach you

An error tracker you never open is the same as no error tracker. In Sentry, go to Alerts and create an alert for your project with:

  • Environment: production only, so local testing never pings you.
  • Triggers: "A new issue is created" and "A resolved issue regresses". The first tells you about anything new. The second tells you when a bug you fixed comes back, which happens a lot when AI rewrites a file.
  • A second alert for spikes: trigger "An event or issue activity is captured", then add a frequency filter: number of events more than, say, 10 in one hour. Pick a number that fits your traffic.
  • Action: the free plan sends email. Add an inbox filter so Sentry emails are never archived, muted or sent to spam.
  • Throttle: at most once every 30 or 60 minutes per issue, so one bad bug does not send 200 emails.

Use the "Send Test Notification" button before saving, and confirm the email actually lands in your inbox, not spam.

Step 6: Add an outside uptime check

Sentry only sees errors from code that runs. If your host is down, your domain expired, or your database is unreachable, there may be nothing to report. You need something outside your app knocking on the door.

First, add a tiny health endpoint that also touches the database. For Next.js:

// app/api/health/route.ts
import { createClient } from "@supabase/supabase-js";

export const dynamic = "force-dynamic"; // never serve a cached "ok"

export async function GET() {
  const supabase = createClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  );
  const { error } = await supabase.from("habits").select("id").limit(1);
  if (error) return new Response("db-fail", { status: 500 });
  return new Response("ok");
}
Enter fullscreen mode Exit fullscreen mode

For a Lovable or Vite app, ask your AI tool to create the same thing as a Supabase Edge Function named health. Because the uptime robot has no login token, that one function needs verify_jwt = false under [functions.health] in supabase/config.toml. Keep it boring: it reads one row through Row Level Security and returns only "ok" or "db-fail", never data.

Then create a free UptimeRobot account. The free plan includes 50 monitors with checks every 5 minutes, plus keyword monitors. Set up two:

  1. Keyword monitor on your homepage: alert if a word that always appears (your product name or headline) is missing. This catches the "page loads but is blank" problem.
  2. HTTP monitor on /api/health: it alerts when the endpoint returns an error status, which ours does (500) whenever the database check fails.

Five-minute checks are fine for a first SaaS.

Step 7: Make errors readable, then let your AI help fix them

A raw production error often points to something like index-a8f3.js:1:48213, which tells you nothing. Source maps fix that. Next.js users already got this from the wizard. For Vite, run:

npx @sentry/wizard@latest -i sourcemaps
Enter fullscreen mode Exit fullscreen mode

It configures your build to upload source maps to Sentry. Sentry's docs warn that public source maps can expose your source code, so keep the option that deletes .map files after upload. Keep the Sentry auth token in your host's environment variables, never in Git.

Now the fun part. Sentry runs an official MCP server, so your AI tool can read your real errors. In Claude Code:

claude mcp add --transport http sentry https://mcp.sentry.dev/mcp/YOUR-ORG/YOUR-PROJECT
Enter fullscreen mode Exit fullscreen mode

Cursor can add the same URL in its MCP settings, and Lovable lists Sentry as a chat connector. Then use a prompt like this:

Read the newest unresolved Sentry issue in production. Explain in plain
English what the user was doing and why it failed. Propose the smallest
fix, show me the diff before applying it, and add a check so this
error is reported with report() if it happens again.
Enter fullscreen mode Exit fullscreen mode

Finish with a 15-minute weekly habit: open Sentry, sort by users affected, fix the top issue, mark it resolved, and ignore known noise (like browser-extension errors).

Common mistakes

  • Testing only while logged in. Many breaks only hit new users. Run your must-never-break list from a private window with a fresh account after every big change.
  • Leaving Sentry on in development. Your own test errors flood the inbox and train you to ignore alerts. Use the production environment filter.
  • Swallowing errors. A catch block that only logs, or a Supabase call that ignores error, hides exactly the bugs you most need to see.
  • Showing success after failure. If the save failed, say so. A fake checkmark is worse than an error message.
  • Alerting on everything. Too many emails and you will mute them. Start with new issues, regressions and one volume rule.
  • Checking only the homepage. A homepage can load fine while login, the API or the database is down. Monitor the health endpoint too.
  • Sending personal data you do not need. Tag users by ID, not email, and leave Session Replay's default masking on if you enable it. Sentry masks all text and blocks media by default.

Your action summary

  1. Write your must-never-break list and answer "how would I find out today?" for each flow.
  2. Add Sentry to the frontend (wizard for Next.js, @sentry/react for Vite), deploy, and test with a real button.
  3. Add a report() helper and use it wherever Supabase, fetch() or try/catch can fail quietly.
  4. Add Sentry to API routes and Edge Functions, starting with webhooks.
  5. Create production-only alerts for new issues, regressions and spikes, and send a test notification.
  6. Add a /api/health endpoint plus two UptimeRobot monitors.
  7. Upload source maps, connect Sentry to your AI tool, and do a 15-minute review every week.

Your users should never be your monitoring system. Set this up once, and the next time something breaks, you will be the first to know.

Top comments (0)