3 ms·
> In Java, Python, JavaScript, and C# an exception thrown from a finally block overwrites the original exception, and the original exception is lost. Pet peeve
by kentonv 10mo ago
> In Java, Python, JavaScript, and C# an exception thrown from a finally block overwrites the original exception, and the original exception is lost.
Pet peeve of mine: all these languages got it wrong. (And C++ got it extra-wrong.)
The error you want to log or report to the user is almost certainly the original exception, not the one from the finally block. The error from the finally block is probably a side effect of the original exception. Reporting the finally exception obscures information about the root cause, making it harder to debug the problem.
Many of these languages do attach the original exception to the new exception in some way, so you can get at it if you need to, but whatever actually catches and logs the exception later has to go out of its way to make sure to log the root cause rather than some stupid side effect. The hierarchy should be reversed: the exception thrown by `finally` should be added as an attachment to the original exception, perhaps placed in a list of "secondary" errors. Or you could even just throw it away, honestly the original exception is almost always all you care about anyway.
(C++ of course did much worse by just crashing in this scenario. I imagine this to be the outcome of some debate in the committee where they couldn't decide which exception should take priority. And now everyone has internalized this terrible decision by saying "well, destructors shouldn't throw" without seeming to understand that this is equivalent to saying "destructors shouldn't have bugs". WELL OF COURSE THEY SHOULDN'T BUT GOOD LUCK WITH THAT.)
- uncivilized 10mo agoThis part is not correct. I can't speak for the other languages, but in Python the exception that is originally thrown is the one that creates the traceback. If the finally block also throws an exception, then the traceback includes that as additional information. The author includes an addendum, yet he is still wrong about which exception is first raised.
- fooofw 10mo agoI believe this might be slightly imprecise also. The traceback is actually shown based on the last-thrown exception (that thrown from the finally in this example), but includes the previous "chained exceptions" and prints them first. From CPython docs [1]: > When raising a new exception while another exception is already being handled, the new exception’s __context__ attribute is automatically set to the handled exception. An exception may be handled when an except or finally clause, or a with statement, is used. [...] The default traceback display code shows these chained exceptions in addition to the traceback for the exception itself. [...] In either case, the exception itself is always shown after any chained exceptions so that the final line of the traceback always shows the last exception that was raised. So, in practice, you will see both tracebacks. However, if you, say, just catch the exception with a generic "except Exception" or whatever and log it without "__context__", you will miss the firstly thrown exception. [1]: https://docs.python.org/3.14/library/exceptions.html#exception-context https://docs.python.org/3.14/library/exceptions.html#excepti...
- uncivilized 10mo agoThanks for pointing that out. I was thinking about the traceback message and hadn't thought about __context__ or __traceback__.