HIGH Any Node / React application

Errors caught, logged to nowhere, and swallowed

What you'd see: Your codebase is full of catch (e) { console.error(e) } and you have no error tracking service.

§ 01 — What's actually happening

A caught error that is only logged to the console is an error nobody will ever see. Server logs on most hosts are rotated and unread; browser console output exists only on the user's machine. Meanwhile the code carries on as if the operation succeeded, so a failed payment recording or a failed email send looks exactly like a successful one.

Why an AI tool writes it this way

Wrapping risky code in try/catch is correct instinct, and <code>console.error</code> is the obvious thing to put inside it while developing. Nothing in the code ever prompts you to replace it, and by the time it matters there are two hundred of them.

What it costs you

You find out about outages from customers, days late, with no stack trace and no idea how many others were affected. The genuinely dangerous variant is the empty catch, which converts a loud failure into silent data loss.

§ 02 — Before & after

Failures nobody ever hears about

Illustrative code, written for this page — never a client's project.

AI-generated

  • Empty or console-only catch blocks
  • Failures indistinguishable from successes to the caller
  • No alerting, so the first report comes from a customer
try { await sendWelcomeEmail(user); } catch (e) { console.error(e); // nobody is reading this } try { await recordPayment(session); } catch (e) {} // silent data loss return { success: true }; // says success either way

Human-reviewed

  • Errors reported to a service with stack trace and user context
  • The caller is told the operation failed
  • The user sees a real message instead of a frozen button
import * as Sentry from '@sentry/node'; try { await sendWelcomeEmail(user); } catch (err) { Sentry.captureException(err, { tags: { operation: 'welcome_email' }, extra: { userId: user.id } }); await queue.retry('welcome_email', { userId: user.id }); // recoverable } try { await recordPayment(session); } catch (err) { Sentry.captureException(err, { extra: { sessionId: session.id } }); throw new Error('Payment could not be recorded'); // not recoverable — surface it }

§ 03 — Check your own

How to tell in two minutes

You don't need us to run these. If any of them come back the wrong way, you have this problem.

  1. Grep for catch and read every block. Empty ones and console-only ones are the list to work through.
  2. Ask whether each failure is recoverable. Recoverable: report and retry. Not recoverable: report and rethrow, so the caller stops pretending it worked.
  3. Add error tracking — Sentry's free tier is enough for most applications and takes about ten minutes to wire in.
  4. Add one uptime check that loads a real page, not just the health endpoint. A health endpoint returning 200 while the app is broken is a common and expensive blind spot.

Found it in your project? Fixing this one properly usually takes an engineer under an hour. Finding the other nine takes longer — which is what the free health check is for.

Get my free health check

Find out where you actually stand.

Free. No obligation. Reviewed by experienced engineers.

Get my free health check