Hacker News new | ask | show | jobs
by wallyhs 4013 days ago
> No, you shouldn't. What are you going to do any differently with that than you would FileIsLockedException or NetworkIsDownException or HarddriveIsCorruptException? You wouldn't do anything differently. Show the user the ErrorMessage in the dialog and allow them to restart whatever operation was in affect. Let them choose a shorter path, close Excel, plug their Ethernet cable back in, or whatever. Trying to play wack-a-mole is a fools errand.

I don't understand this viewpoint. If a file is locked, the user may be able to fix it, so go ahead and display an error. But if there is corruption, then what is the user going to do? You can't tell them, "Restore your system from backup, and then click OK." Your program has to crash before it causes more problems.

What if the situation is stack corruption or heap corruption? Or what if the error left the application in an inconsistent state? Continuing to run might corrupt the user's data. What if an attacker caused the corruption in order to gain unauthorized access? Logging or displaying the error might be exactly what they want.

I often see variations of this advice: If you don't know how to handle it, don't catch it. By definition, you don't know how to handle an unknown exception.

> Put MSDN away, you don't need it.

I think it is a good idea to understand the behavior and failure modes of every API function that you call; this requires that you consult the documentation.

1 comments

> But if there is corruption, then what is the user going to do?

Corruption is outside the state of this discussion. If half your app just overwrote the other half of your app, no matter what we say here is going to make any difference.

> Or what if the error left the application in an inconsistent state?

That's a good question. The other part (and in my opinion the important part) of exception handling is ensuring your stack unwinds correctly. Managed languages help this with garbage collection, resource-using statements, and finally clauses. C++ does it with RAII. This is typically just your normal resource cleanup; if you are cleaning up correctly without exceptions you'll cleanup correctly with exceptions.

> I think it is a good idea to understand the behavior and failure modes of every API function that you call; this requires that you consult the documentation.

You are right, of course. You should definitely know the behavior and failure modes of every framework and library you call. And you should consult the documentation for that. But what you don't need to consult (or even necessary know) is the mapping of every method to every exception. You should look at the total set of all exceptions and handle the ones you can handle.

> Corruption is outside the state of this discussion.

Why? If corruption leads to an exception, it seems relevant. Corruption could be the application's own fault, it could be failing hardware, it could be someone trying to exploit a vulnerability. It could be anything - a result of undefined behavior. Sorry if I'm using the term loosely.

> If half your app just overwrote the other half of your app, no matter what we say here is going to make any difference.

If it overwrote the other half, it should crash immediately. I can't just display an error message and retry the operation in that case. If I don't know what exception I'm handling, how can I be sure that I won't cause more damage by handling it?

> The other part (and in my opinion the important part) of exception handling is ensuring your stack unwinds correctly.

That is not what I was referring to. A program can still get into an inconsistent state with exceptions. For example, if a function updates half of some data structure and then throws, that data structure may be left in an inconsistent state. Of course you should not write functions that way, but it is an easy mistake to make. Do you really want your app to keep running in an inconsistent state?

I'm looking at it from a defensive, assume-the-worse perspective. If my application is in an inconsistent state, it should cease running immediately. If my application does not know which state it is in, it must assume that it is in an inconsistent state.

This has nothing to do with exceptions. If some function updates half of some structure and then just returns due to a bug then you have an inconsistent state and no exceptions were involved at all. And your application keeps running in an inconsistent state.
You can only recover from such errors locally. The local context is not available in a global exception handler.
If your code corrupted your data structure or a cosmic ray changed a bit, you're not recovering no matter what.
That's my point: in this situation, you can't display an error and keep going. How do you know that you are not in this situation when you catch an unknown exception?
>You are right, of course. You should definitely know the behavior and failure modes of every framework and library you call. And you should consult the documentation for that. But what you don't need to consult (or even necessary know) is the mapping of every method to every exception. You should look at the total set of all exceptions and handle the ones you can handle.

You keep stating this, but it doesn't make sense. By definition, for you to handle it somewhere requires knowing the entry point to the call chain that may throw exception(s) that you decide to handle. At this point, you're wrapping some method invocation with a catch block, are you not? If so, you need to know that this method throws these exceptions. Sure, in some cases you may not care which of the tens of methods in the call chain actually triggered a SocketException, but that's a specific case and doesn't solve the overall problem. What if there are several sockets involved in the operation and you want to reconnect just the failing one, for example? At some point, you're going to have to sink your error handling to the point where you have enough context to handle it, which invariably means it's not enough to know the general types of exceptions an entire framework/library throws.

> By definition, for you to handle it somewhere requires knowing the entry point to the call chain that may throw exception(s) that you decide to handle. At this point, you're wrapping some method invocation with a catch block, are you not?

I think you're still missing the point and maybe I'm not explaining it well. For you to handle an exception, you don't need to know where something may throw an exception. You just need to know where you can handle the error. For example, if I'm iterating a data set and performing a web service call per iteration then I'm more than likely going to put my exception handling inside that iteration around that service call. My recovery is more than likely going to be retrying the service call a few times and then give up and iterate to the next item. Perhaps enough overall failures will terminate the whole loop.

So yes, at some point I will be wrapping some method invocation with a catch block. Does that method throw an exception? Yes. All methods throw exceptions.

> What if there are several sockets involved in the operation and you want to reconnect just the failing one, for example?

Obviously, you do need to put the exception handling code where you can reasonably handle the error. Which is just restating my point. If your code generates several sockets and performs operations on them then the error handling should be connected to those operations. So again the decision about where to put the error handling is not based on which methods throw (they all throw) it's about where it makes the most sense to handle it.

I believe exception handling should build up from the generic to the more specific as needed. If you have to handle every single exception at every call site then that's building it down from the most specific case. Java forces you to do this and it's almost always a waste of time and mental effort. It also makes iterative design difficult. Most first versions of my applications have just one single handler that logs everything and terminates. Many times that's good enough. But where it's not good enough, the next version can be easily improved with specifically targeted exception handling sprinkled in as needed.