Skip to main content

Error Handling Without Empty Try/Catch Theater

How to handle errors in a way that helps the people who operate, debug, and evolve the system instead of hiding failure behind random fallbacks.

Andrews Ribeiro

Andrews Ribeiro

Founder & Engineer

The problem

A lot of code “handles errors” like this:

try {
  process()
} catch (error) {
  console.log(error)
}

That looks safe.

It is not.

In practice, that kind of block often:

  • swallows context
  • hides the real failure
  • lets the flow continue in a broken state
  • makes debugging harder later

In other words, the error did not disappear. It just became worse to understand.

Mental model

Think about it like this:

handling an error is not silencing an exception; it is giving the failure the right destination

That destination changes depending on the case:

  • log it
  • propagate it
  • translate it
  • abort the flow
  • try again

The important part is making that decision with criteria. Not by reflex.

Breaking it down

First understand what kind of error this is

Some questions help:

  • is this a business error or a technical error?
  • is it an expected error or an anomaly?
  • is it temporary or permanent?
  • can the user act on it?

Without that separation, every error becomes one amorphous mass.

Then decide what to do with it

The most common options are:

  • log it with context
  • return a clear response
  • stop the flow
  • propagate it to an upper layer
  • apply a retry with criteria

The mistake here is using the same answer for everything.

Preserve context

If you catch an error and replace it with something else without preserving context, debugging gets expensive.

Replacing:

payment_provider_timeout request_id=...

with:

something went wrong

is a very efficient way to hate your own future 3 AM incident.

Do not turn catch into a hiding place

Using catch only because “what if it fails” is a weak pattern.

If the current layer does not know how to resolve the failure, many times it should:

  • add context
  • and pass it along

Not pretend it solved it.

Simple example

Imagine one call to a payment gateway.

Weak handling:

  • catch any error
  • log one generic string
  • return false

Now the caller does not know:

  • was it a timeout?
  • was the card declined?
  • were the credentials invalid?
  • is a retry useful?

Better handling:

  1. distinguish business error from technical error
  2. log useful context
  3. return something to the upper layer that allows the right reaction

Example:

  • declined card: business response, no retry
  • provider timeout: technical error, maybe retry or temporary-unavailable message
  • invalid credential: configuration incident, needs operational alert

The code is still simple. But the system becomes much easier to operate.

Common mistakes

  • using a generic catch and continuing the flow as if nothing happened
  • losing the original stack trace or error while translating the exception
  • retrying an error that will never improve on its own
  • showing too much internal detail to the end user
  • mixing expected business errors with serious technical failure

How a senior thinks

More experienced engineers think about the whole path of the failure.

The reasoning often sounds like this:

“If this goes wrong, who needs to know? What can actually be done in this layer? What context do I need to preserve so the next person or the next layer can understand what happened?”

That mindset avoids two bad extremes:

  • exploding everything without criteria
  • swallowing everything without criteria

What the interviewer wants to see

In interviews, this topic measures technical judgment.

The evaluator wants to see whether you:

  • distinguish types of error
  • preserve context
  • know when to propagate
  • do not use try/catch as cosmetic robustness

A strong answer often sounds like this:

“I would try to separate expected business errors from technical failure. The layer that catches the error needs either to actually resolve it or to add context and propagate it. What I would avoid is a generic catch that hides the cause and leaves the rest of the system unable to react correctly.”

Good error handling does not make the problem disappear. It makes the problem more governable.

An empty catch is the fastest way to trade a clear error for a bad investigation.

Quick summary

What to keep in your head

Practice checklist

Use this when you answer

You finished this article

Next article Intermittent Bugs: Where to Start Previous article Distributed Tracing: What It Is and How to Use It to Debug Systems

Keep exploring

Related articles