Course contents

Custom exceptions and best practices

Designing a service exception hierarchy (domain versus infrastructure), checked or unchecked, preserving causes, useful messages, logging once, HTTP mapping, retryable errors, interrupts, exception cost and stackless exceptions.

PremiumOften askedLesson18 min readCoreReviewed Oct 2026 · Java 21
On this page
  1. Picture it first: a hospital triage desk
  2. Designing the hierarchy, under the hood
  3. Real world: translate low, log once, map at the boundary
  4. Advanced: the rules senior rounds probe
  5. How to say it in the interview

🧯 What is a custom exception?

A custom exception is a class you write, extending Exception or RuntimeException, that gives a failure in your system a name and data. Instead of throwing RuntimeException("not enough money"), a payment service throws InsufficientFundsException carrying the account id and the shortfall of ₹250, so the caller can react to the type and the fields instead of parsing a string.

Writing the class takes thirty seconds. Designing the exceptions of a whole service is where interviews get interesting: which failures deserve their own type, checked or unchecked, where you log, how a timeout three layers down becomes a 503 with a Retry-After header, and what you do with an InterruptedException you didn't ask for. Interviewers use this topic to see whether you've owned a production service, because every one of these questions has a 2 a.m. incident behind it.

What you’ll learn

  • Design a small exception hierarchy split into domain and infrastructure failures, with an error code and a retryable flag
  • Choose checked or unchecked with a reason, and know when to reuse IllegalArgumentException and friends instead
  • Preserve the cause with exception translation, write messages that help on-call, and avoid log-and-rethrow
  • Map exceptions to HTTP status codes at one boundary with ProblemDetail, and retry only retryable, idempotent calls
  • Handle InterruptedException correctly by propagating it or restoring the flag, and never catch Throwable casually
  • Explain why fillInStackTrace() makes exceptions expensive, and when a stackless exception is worth it

For this lesson, we'll design an exception hierarchy for a payment service (domain versus infrastructure failures), pick checked or unchecked, preserve causes, write messages that help on-call, translate errors to HTTP status codes at the boundary, mark errors as retryable, and then go deep on the cost of exceptions and the stackless trick for hot paths.

Below the line (out of scope)

The 30-second answer

I give a service one small exception hierarchy: an abstract base that carries an error code and a retryable flag, a domain branch for broken business rules (insufficient funds, order not found) and an infrastructure branch for failed dependencies (gateway timeout, database down). I make them unchecked by default, always pass the original exception as the cause, and put ids and values in the message. Lower layers translate low-level exceptions into these types and add context but don't log. One handler at the boundary logs once and maps the type to an HTTP status: domain to 4xx, infrastructure to 5xx. I never swallow InterruptedException without restoring the interrupt flag, never catch Throwable casually, and never use exceptions for normal control flow, because building the stack trace is the expensive part.

Interview radar

Asked very often: "How do you create a custom exception, and should it be checked or unchecked?" and "Why should you pass the cause?" Senior rounds probe: where you log, how you map exceptions to HTTP codes and retries, what you do with InterruptedException, and why exceptions are slow. Production angle: one gateway timeout logged four times by four layers, or a swallowed interrupt that stops a pod from shutting down cleanly.

01Picture it first: a hospital triage desk

Think of the exception handler at your API boundary as a triage desk. Patients (failures) arrive from every corridor of the building. The nurse doesn't read a long story from each patient; she reads the wristband. "Broken rule, patient's own doing" goes to one ward (a 4xx, don't retry). "Equipment failure, come back in a minute" goes to another (a 5xx, retry with backoff). A custom exception type is that wristband. If every failure arrives wearing the same blank band (RuntimeException), the desk can only guess.

Here's the hierarchy I'd sketch for a payment service, with the triage decision written under each leaf:

Fig. 1

A payment service exception hierarchy: RuntimeException at the top, an abstract PaymentException carrying errorCode and retryable, a domain branch with InsufficientFunds, CardDeclined and PaymentNotFound mapped to 4xx with no retry, and an infrastructure branch with GatewayTimeout and LedgerUnavailable mapped to 503 with retry

A payment service exception hierarchy: RuntimeException at the top, an abstract PaymentException carrying errorCode and retryable, a domain branch with InsufficientFunds, CardDeclined and PaymentNotFound mapped to 4xx with no retry, and an infrastructure branch with GatewayTimeout and LedgerUnavailable mapped to 503 with retry

Notice three things. The split that matters is who can fix it: the caller (domain) or time (infrastructure). The base class carries the fields every handler needs, so the boundary code never has to know all the leaves. And the tree is shallow: two levels below the base is plenty for most services.

02Designing the hierarchy, under the hood

Try it yourself first

Before reading on, sketch the exception classes for a PaymentService that calls an external card gateway and an internal ledger. Which ones does a caller need to tell apart? What fields does each carry?

Here's how I'd write it. The base class is abstract, so nobody throws a vague PaymentException:

Premium lesson

Keep reading with the Java Pass

The rest of this lesson is part of Java One-Stop Interview Prep: in-depth explanations, diagrams, model answers and how to say them in the interview.

  • Real world: translate low, log once, map at the boundary
  • Advanced: the rules senior rounds probe
  • How to say it in the interview

From ₹199 · one-time payment · No auto-renewal

Practise this topicExceptions: interview questions & scenarios
Further reading (7)