203

In Javascript, suppose I want to perform some cleanup when an exception happens, but let the exception continue to propagate up the stack, eg:

try {
  enterAwesomeMode();
  doRiskyStuff(); // might throw an exception
} catch (e) {
  leaveAwesomeMode();
  throw e;
}
doMoreStuff();
leaveAwesomeMode();

The problem with this code is that catching and rethrowing the exception causes the stack trace information up to that point to be lost, so that if the exception is subsequently caught again, higher up on the stack, the stack trace only goes down to the re-throw. This sucks because it means it doesn't contain the function that actually threw the exception.

As it turns out, try..finally has the same behavior, in at least Chrome (that is, it is not the re-throw that is the problem precisely, but the presence of any exception handler block at all.)

Does anyone know of a way to rethrow an exception in Javascript but preserve the stack trace associated with it? Failing that, how about suggestions for other ways to add exception-safe cleanup handlers, while also capturing complete stack traces when an exception happens?

Thanks for any pointers :)

Anurag
  • 140,337
  • 36
  • 221
  • 257
Geoff
  • 4,372
  • 5
  • 25
  • 29

4 Answers4

97

This is a bug in Chrome. Rethrowing an exception should preserve the call trace.

http://code.google.com/p/chromium/issues/detail?id=60240

I don't know of any workaround.

I don't see the problem with finally. I do see exceptions silently not showing up on the error console in some cases after a finally, but that one seems to be fixed in development builds.

Glenn Maynard
  • 55,829
  • 10
  • 121
  • 131
35

The stack property of an Error object is created at the same time as the Error object itself, not at the point it's thrown. They're often the same because of the idiom

   throw new Error("message");

and if you use the code just as you've written it, the stack property will not be changed when you rethrow the error.

jpaugh
  • 6,634
  • 4
  • 38
  • 90
Mike Stay
  • 1,071
  • 8
  • 17
  • 7
    This is not true (maybe platform dependent). The js engine i'm using now (Rhino) resets the stack on the throw statement, losing the original stack. – Ted Bigham Oct 06 '17 at 19:26
  • 1
    Perhaps so, but rhino-1.7.7.2.jar does not change it. What version are you using? – Mike Stay Oct 07 '17 at 05:11
6

As mentioned, the stack is a snapshot created while running new Error(...), so you can't really throw the error with the same stack.

A workaround I used is to console.error the stack before throwing:

  console.error(err.stack);
  throw err;

It's not perfect, but it gets you enough debug-able information, and the stack of the original error.

Ofer Segev
  • 5,094
  • 2
  • 22
  • 22
-2

Don't catch it in the first place, just use finally

try {
  enterAwesomeMode();
  doRiskyStuff(); // might throw an exception
  doMoreStuff();
} finally {
  leaveAwesomeMode();
}
texuf
  • 485
  • 5
  • 9
  • 2
    If you need to do some conditional processing in your catch-section this may not be an option, e.g., check for some condition and handle that, otherwise rethrow the same exception. – Jan Hansen Oct 13 '22 at 07:02
  • 1
    or you want to log the error but continue – zumalifeguard Nov 02 '22 at 16:07
  • The question was how to rethrow the exception and preserve the callstack. If you're logging then continuing, none of this applies And Jan, yes, if you want to add some tricky logic in your catch, then this might not apply, but that was not part of the original question – texuf Nov 02 '22 at 22:13