Mesrai
Back to blog
// essayTechnical Deep Dive

The Silent catch {} Mesrai Refused to Approve

Real PR catch: empty catch swallowing all errors in a sync job. Log + metric + continue fix.

Mesrai TeamAugust 25, 20268 min read

A sync job with `catch {}`. Errors silently disappear. Operators see no failures, but data is stale. Mesrai flagged the pattern.

The vulnerable diff

tsservices/sync.ts
// services/sync.ts
async function syncAll(items: Item[]) {
  for (const item of items) {
    try {
      await syncOne(item);
    } catch {}   // BUG: swallows everything
  }
}

What is wrong

Empty catch is the most aggressive form of error suppression. It hides validation errors, transient network failures, business-logic errors, and unrelated system errors equally. The original author likely meant 'ignore one bad item, keep processing' but the cost is invisibility — no logging, no metric, no alert. CWE-755 covers improper handling of exceptional conditions. The fix is to log every caught error and re-throw what the caller cannot recover from.

The attack

Symptom: 'why is the sync running but the data not updating?'

textexploit
# Logs: 'sync complete' every 5 minutes.
# Reality: 80% of items errored out silently.
# Found via downstream data team comparing source vs destination.

Months to detect, hours to fix.

Mesrai's review comment

textreview-comment.txt
mesraipilot · Bot · reviewed 30 sec ago

[mesrai] [code-review] [Logic] [Silent-Catch] [medium]

Empty catch hides every error. At minimum, log:

  try { await syncOne(item); }
  catch (err) {
    log.error({ err, itemId: item.id }, "sync failed");
    // optional: continue, throw, or track for retry
  }

If you genuinely want to continue on failure, also emit a metric so
the rate is observable:

  metrics.increment("sync.item.failed");

Never `catch {}` — even in a one-off script.

The fix

tsservices/sync.ts (fixed)
// services/sync.ts — fixed
async function syncAll(items: Item[]) {
  let failed = 0;
  for (const item of items) {
    try {
      await syncOne(item);
    } catch (err) {
      log.error({ err, itemId: item.id }, "sync failed");
      metrics.increment("sync.item.failed");
      failed++;
    }
  }
  if (failed > 0) log.warn({ failed, total: items.length }, "sync had failures");
}

Every caught error is logged, counted, and the totals are surfaced at end. Operators see the failure rate. The job continues on individual failures (intentional) but nothing is invisible.

Why human review missed it

Silent catch is the easiest pattern to write and the hardest to debug later. Mesrai catches every empty catch block in non-test code.

Related rules + further reading

Mesrai rule pack: logic/no-silent-catch — flags empty catch blocks.

CWE-755 — Improper Handling of Exceptional Conditions.

ESLint: no-empty (with allowEmptyCatch: false).

Takeaway

Catch must log. Or re-throw. Or both. Never empty. Mesrai catches every silent catch.

// try

See it on your next PR.

Free for individuals. Install in two minutes. Mesrai reviews every commit.