A new error thrown inside catch
An error thrown inside a catch is not handled by that same catch. It travels up the call stack to the nearest outer try...catch, and if there is none it terminates the program.
Theory
TL;DR
- A
catchonly catches what happened in its owntryblock. - An error thrown from the
catchbody goes up the stack to the next handler. - With no outer handler you get an uncaught error and the program dies.
finallyalways runs, even when thecatchthrew a new error.- To handle the error locally, wrap the
catchbody in its owntry...catch.
Quick example
try {
throw new Error("failed in try");
} catch (err) {
console.log("caught:", err.message);
// a new error right here
throw new Error("failed inside catch");
}Output:
caught: failed in try
Uncaught Error: failed inside catchThe first error was caught, the second one had no handler and brought the program down.
What happens step by step
- An error is thrown in
try, control moves tocatch. - The handling code inside
catchstarts running. - If an error happens again inside
catch, the currentcatchno longer catches it: it is responsible only for errors from its owntry. - The new error goes up the stack, to the nearest outer
try...catchif there is one. - If there is no outer
try...catch, the program terminates with that error.
An outer try...catch catches the error from catch
To handle the new error you need another construct one level up:
try {
try {
throw new Error("failed in try");
} catch (err) {
console.log("inner catch:", err.message);
throw new Error("failed in catch");
}
} catch (err) {
console.log("outer catch caught:", err.message);
}Output:
inner catch: failed in try
outer catch caught: failed in catchNow the outer construct caught the error that happened inside the catch block.
Here is what happens without an outer handler, the error simply takes the script down:
try {
throw new Error("first failure");
} catch (e) {
console.log("catch:", e.message);
JSON.parse("invalid json"); // SyntaxError
}Output:
catch: first failure
Uncaught SyntaxError: Unexpected token 'i', "invalid json" is not valid JSONfinally runs no matter what
Even if a new error appeared inside catch, the finally block still runs before the error surfaces.
try {
try {
throw new Error("failed in try");
} catch (e) {
console.log("catch:", e.message);
throw new Error("failed in catch");
} finally {
console.log("finally: always runs");
}
} catch (e) {
console.log("outer catch:", e.message);
}Output:
catch: failed in try
finally: always runs
outer catch: failed in catchThe order matters: finally first, and only then does the error reach the outer catch.
A nested try inside catch
Another option is to wrap the body of catch in its own try...catch:
try {
throw new Error("failed in try");
} catch (e) {
try {
console.log("catch:", e.message);
throw new Error("failed inside catch");
} catch (innerErr) {
console.log("caught the inner one:", innerErr.message);
}
}Output:
catch: failed in try
caught the inner one: failed inside catchThis keeps errors raised inside catch under control so that nothing escapes.
Summary table
| Scenario | What happens |
|---|---|
An error inside catch | The current catch does not catch it |
There is an outer try...catch | The error is passed there |
There is no outer catch | The program crashes |
There is a finally | It runs anyway |
The catch body has its own try | The inner error is caught locally |
Common mistakes
- Assuming a
catchwill intercept its ownthrow. The handler is already active and does not catch itself. - Forgetting that after a
throwincatchthe rest of thecatchbody never runs. - Thinking a new error in
catchcancelsfinally.finallyalways runs. - Throwing a new error and losing the original cause. Pass it along:
throw new Error("wrapper", { cause: err }). - Throwing from
finally. Such athrowoverrides the exception that was already on its way out, and the original error disappears without a trace.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.