What is the difference between exception translation, exception chaining, and exception masking (swallowing)?
answer
- Translation = type, chaining = cause, masking = loss
- Translate AND chain; never mask
- finally throwing hides the real exception
- try-with-resources → addSuppressed, nothing lost
- 'Caused by:' vs 'Suppressed:'
basics
~20 sTranslation = throw a new, higher-level exception. Chaining = attach the original as the cause of that new exception. Masking = throw or catch in a way that loses the original. You usually translate AND chain, and never mask.
solid answer
~50 sThese three terms are often confused. Exception translation is converting a low-level exception into a higher-level, layer-appropriate one — it's about the *type* you expose. Exception chaining is the mechanism of attaching the original exception as the cause of the new one, via new Wrapper(msg, cause) or initCause, so the 'Caused by:' trace survives. They're complementary: good translation always uses chaining. Exception masking (or swallowing) is the anti-pattern where the original is lost — either by catching and ignoring it, or by throwing a new exception without passing the cause, or by a finally/try-with-resources interaction that overwrites it. A classic masking bug is when a finally block throws, hiding the real exception from the try; Java's suppressed-exception mechanism (Throwable.addSuppressed, used by try-with-resources) exists to prevent exactly that. The rule: translate when it adds abstraction value, chain so nothing is lost, never mask.
go deeper
Knows that ignoring an exception (empty catch) is bad and that you should keep the original somehow.
Clearly distinguishes translation (type), chaining (cause), and masking (loss); always chains when rethrowing.
Explains the finally-overwrites trap and how try-with-resources / addSuppressed solves it; knows suppressed vs caused-by.
Sets team conventions and lint rules to forbid swallowing/masking, reasons about how cause/suppressed chains surface in logging and monitoring at scale.
## Three terms, three meanings These words get used interchangeably in interviews but mean distinct things. ### 1. Exception translation **What:** catching a low-level exception and throwing a *different, higher-level* exception that fits the current abstraction layer. It's about the **type** the caller sees. Example: catch `SQLException`, throw `DataAccessException`. ### 2. Exception chaining **What:** the **mechanism** of linking the original exception to the new one as its *cause*. In Java, every `Throwable` has an optional `cause`. You set it via: - the standard constructor: `new MyException("message", originalException)` - or `myException.initCause(originalException)` if no such constructor exists. The cause is printed in stack traces as a `Caused by:` block. Chaining is what makes translation safe — you change the type *without throwing away* the evidence. Translation and chaining almost always go together: you translate the type and chain the cause. ```java try { parse(input); } catch (NumberFormatException e) { throw new ConfigException("Invalid port in config", e); // translate (new type) + chain (e as cause) } ``` ### 3. Exception masking (a.k.a. swallowing / shadowing) **What:** an **anti-pattern** where the original exception's information is *lost*. Forms: - **Swallowing:** `catch (Exception e) { }` — caught and ignored entirely. The error vanishes. - **Translating without chaining:** `throw new ConfigException("bad config");` — a new exception is thrown but the original `e` is not passed as cause, so you lose the real reason. - **Overwriting in finally:** if the `try` block throws exception A and the `finally` block then throws exception B, the JVM propagates B and **A is lost**. The same can happen when closing a resource throws while the body already failed. ## The finally/close masking trap and 'suppressed' exceptions Consider: ```java try { throw new IOException("real problem"); } finally { throw new RuntimeException("cleanup failed"); // this WINS; IOException is lost } ``` The `IOException` — the real cause — is silently discarded. To address this, Java 7 added the **suppressed-exception** mechanism: `Throwable.addSuppressed(Throwable)` and `getSuppressed()`. **try-with-resources** uses it automatically: if the body throws and then `close()` also throws, the body's exception propagates as primary and the `close()` exception is attached as a *suppressed* exception (shown as `Suppressed:` in the trace) — nothing is lost. This is why try-with-resources is preferred over manual try/finally for closing resources. ## How they relate - **Translation + chaining = the correct pattern.** Change the type for abstraction cleanliness; keep the cause for diagnostics. - **Translation WITHOUT chaining = masking.** A surprisingly common bug — you intended to translate but destroyed the evidence. - **Chaining without translation** is possible (wrap in a generic `RuntimeException(e)`) but pointless if the wrapper adds no abstraction value; it just deepens the trace. ## Practical rules 1. Never swallow: an empty catch must be justified with a comment and usually logged. 2. Always pass the cause when rethrowing. 3. Use try-with-resources so cleanup failures become *suppressed*, not masking, exceptions. 4. Don't let `finally` throw; if cleanup can fail, handle it inside the finally.
- What is a suppressed exception and when does it occur?A secondary exception attached via Throwable.addSuppressed, shown as 'Suppressed:' in the trace. try-with-resources adds the close() failure as suppressed when the body already threw, so the primary exception isn't masked.
- Show a masking bug that translation introduces.throw new ConfigException("bad config") inside a catch(IOException e) without passing e — the IOException's real reason and stack are lost. Fix: throw new ConfigException("bad config", e).
saying these in an interview costs you the question
- Saying translation and chaining are the same thing
- Thinking an empty catch block is acceptable without justification
- Not knowing that a throwing finally overwrites the real exception
- Confusing suppressed exceptions with the cause chain