views:

1012

answers:

14

What are some of the most common mistakes you've seen made when handling exceptions?

It seems like exception handling can be one of the hardest things to learn how to do "right" in .Net. Especially considering the currently #1 ranked answer to Common programming mistakes for .NET developers to avoid? is related to exception handling.

Hopefully by listing some of the most common mistakes we can all learn to handle exceptions better.

+4  A: 

Empty catch:

//What's the point?
catch()
{}

Rethrowing:

//Exceptions are for *adding* detail up the stack
catch (Exception ex)
{throw ex;}
Dave Swersky
Empty catch is bad programming practice? You would suggest it is a bad idea to do the following?Suppose there is a program that displays weather information on the screen. Every five minutes, it connects to a database to retrieve a weather update and displays the new weather report along with the time of the last weather report retrieved.It would then be inappropriate to put the database connection inside a `Try` with an empty `Catch` so that the program could continue to display the weather from five minutes ago if the program fails to get a new weather report?
Rice Flour Cookies
@Rising Star: In that case you should log a database error.
BlueRaja - Danny Pflughoeft
@Rising Star absolutely it would be bad, that catch should log the event and/or make the user aware that the next report is delayed
Pharabus
Empty catch is bad 99% of the time. I've seen one or two examples where it's acceptable. There are exceptions (pun unintended!) to every rule (well, *almost* every rule; there are exceptions to the rule that there are exceptions to every rule...)
Charles
@Rising Star: you should not use an empty catch in that case. You should catch the kind of exceptions that can come from your database operations, and nothing else. You're trying to ignore database exceptions, not all exceptions. You don't want to be ignoring `NullReferenceException`, for instance.
John Saunders
Umm...what happens when your logging code throws an exception. Of course you are going to swallow that!
Brian Gideon
@Brian: You could f.e. log this exception into windows event log ;)
Tim Schmelter
@Tim: What happens if that errors out? :)
Brian Gideon
Oh, by the way, I forgot..+1: Generally speaking an empty catch isn't such a great idea.
Brian Gideon
+15  A: 

Re-throwing exceptions like this:

try 
{ 
   // some code here
}
catch(Exception ex)
{
   // logging, etc
   throw ex;
}

This kills the stack trace, making is far less usable. The correct way to rethrow would be like this:

try 
{ 
   // some code here
}
catch(Exception ex)
{
   // logging, etc
   throw;
}
Anna Lear
He mentioned this in the question..
BlueRaja - Danny Pflughoeft
@BlueRaja: I didn't actually click the link. Ah well. Can't hurt to have it mentioned here, since it's still related to this question.
Anna Lear
"The correct way" is a little extreme; there are valid cases for `throw ex`.
Dour High Arch
@Dour High Arch: Such as? I'm not saying there aren't any; just can't think of an example.
Anna Lear
@Anna: Whenever you want to throw a business rule, invariant, or contract and not whatever test the code uses to implement the condition. Also exceptions with security or privacy implications. You want to throw "Contractor not specified", "Multicast not supported", "I have no record of that account" not "File not found", "Null reference", "User '[email protected]' password 'Imwearinglingere' not found".
Dour High Arch
@Dour: Wouldn't you wrap the original exception inside your new one in those cases, though? Something like, "throw new ContractorNotSpecifiedException(fileNotFoundEx)", so that the original FileNotFound exception is propagated inside the InnerException property of your more contextually meaningful exception. In this case you aren't re-throwing the same exception. You're throwing a new one, using the original as a base that can be explored for more information (e.g. a full stack trace).
Anna Lear
@Anna Sometimes for security reasons you don't want your low level framework bubbling up certain information. In those cases you can use throw ex to reset the stack trace.
AaronLS
@Dour: `throw ex;` is not valid in your case. `throw new SaferException(...)` is what you want.
John Saunders
+10  A: 

Catching all exceptions when in many cases you should attempt to catch specific exceptions:

try {
  // Do something.
} catch (Exception exc) {
  // Do something.
}

Rather than:

try {
  // Do something.
} catch (IOException exc) {
  // Do something.
}

Exceptions should be ordered from most specific to least.

Jay Riggs
+7  A: 

Not using using on IDisposable objects:

File myFile = File.Open("some file");
callSomeMethodWhichThrowsException(myFile);
myFile.Close();

myFile does not get closed until myFile's finalizer is called (which may be never) because an exception was thrown before myFile.Close() was called.

The proper way to do this is

using(File myFile = File.Open("some file"))
{
    callSomeMethodWhichThrowsException(myFile);
}

This gets translated by the compiler into something like:

File myFile = File.Open("some file");
try
{
    callSomeMethodWhichThrowsException(myFile);
}
finally
{
    if(myFile != null)
        myFile.Dispose(); //Dispose() calls Close()
}

So the file gets closed even in the face of exceptions.

BlueRaja - Danny Pflughoeft
A: 

Wrong

try
{
   // Do something stupid
}
catch
{
   // ignore the resulting error because I'm lazy, and later spend
   // a week trying to figure out why my app is crashing all over
   // the place.
}

Better

try
{
    /// do something silly.
}
catch (InvalidOperationException ex)
{
    /// respond, or log it.
}
catch (Exception e)
{
    /// log it.
}
David Lively
+9  A: 

Rethrowing an exception with a meaningless message.

try
{
    ...
}
catch (Exception ex)
{
   throw new Exception("An error ocurred when saving database changes").
}

You won't believe how often I see code like this running in production.

Carlos Loth
Another bad in this example is that the original exception is not passed as an inner exception to the new exception object. Probably even worse than the meaningless error message, at least the inner exception would have provided a clue to the original problem.
Chris Taylor
This is often a *good* idea if the original exception contains sensitive information about the implementation details of the server, and the caller who will be handling the exception is not trusted. An attacker attempting to, say, craft a SQL injection attack against a database would *love* to see a detailed error message describing exactly how the query went wrong.
Eric Lippert
Agreed with Eric's comment, so we should consider the security concerns when deciding to include or not the inner exception info.. However, I most of the code I see actually hides the underlying cause of the exception, that is why I created another answer about including the inner exception. I'll include a comment about it there. Thanks.
Carlos Loth
+3  A: 

Assuming an exception that covers many scenarios was something specific. A real life scenario was a web app where the exception handling always assumed all errors were session time outs and logged and reported all errors as session time outs.

Another example:

try
{
     Insert(data);
}
catch (SqlException e)
{
   //oh this is a duplicate row, lets change to update
   Update(data);
}
MatthewMartin
+5  A: 

Forget to set the inner exception when rethrowing a catched exception

try
{
    ...
}
catch (IOException ioException)
{
    throw new AppSpecificException("It was not possible to save exportation file.")
    // instead of
    throw new AppSpecificException("It was not possible to save exportation file.", ioException);
}

When I posted this answer, I forget to mention that we should always consider when to include inner exception or not due to security reasons. As Eric Lippert pointed out on another answer for this topic, some exceptions can provide sensitive information about the implementation details of the server. Thus, if the caller who will be handling the exception is not trusted, it is not a good idea to include the inner exception information.

Carlos Loth
+1 for the inner exception :)
Chris Taylor
hadn't thought of that. thanks!
senloe
Bit of a bad example here - it's recommend to not use ApplicationException
ScottE
I changed the sample from using ApplicaationException to AppSpecificException. Thanks for point this out.
Carlos Loth
+3  A: 

To log Exception.Message instead of Exception.ToString()

Many times, I see code logging only the exception message while it should log the return of ToString method. ToString provides much more information about the exception than the Message. It includes information like inner exception and stack trace besides the message.

Carlos Loth
+1  A: 

Trying to catch OutOfMemoryException or StackOverflowException - those lead to a shutdown of the runtime, hence to way to catch them from within the same Process (or even from the CLR as a whole?)

OutOfMemoryException: The exception that is thrown when there is not enough memory to continue the execution of a program.

"Starting with the .NET Framework version 2.0, a StackOverflowException object cannot be caught by a try-catch block and the corresponding process is terminated by default. Consequently, users are advised to write their code to detect and prevent a stack overflow."

Michael Stum
+33  A: 

What are some of the most common mistakes you've seen made when handling exceptions?

I can think of lots.

First read my article on categorization of exceptions into vexing, boneheaded, fatal and exogenous:

http://blogs.msdn.com/ericlippert/archive/2008/09/10/vexing-exceptions.aspx

Some common errors:

  • Failure to handle exogenous exceptions.
  • Failure to handle vexing exceptions.
  • Construction of methods that throw vexing exceptions.
  • Handling exceptions that you actually cannot handle, like fatal exceptions.
  • Handling exceptions that hide bugs in your code; don't handle a boneheaded exception, fix the bug so that it isn't thrown in the first place

Security error: failure to the unsafe mode

try
{
  result = CheckPassword();
  if (result == BadPassword) throw BadPasswordException();
}
catch(BadPasswordException ex) { ReportError(ex); return; }
catch(Exception ex) { LogException(ex); }
AccessUserData();

See what happened? We failed to the unsafe mode. If CheckPassword threw NetworkDriverIsAllMessedUpException then we caught it, logged it, and accessed the user's data regardless of whether the password was correct. Fail to the safe mode; when you get any exception, assume the worst.

  • Security error: production of exceptions which leak sensitive information, directly or indirectly.

This isn't exactly about handling exceptions in your code, it's about producing exceptions which are handled by hostile code.

Funny story. Before .NET 1.0 shipped to customers we found a bug where it was possible to call a method that threw the exception "the assembly which called this method does not have permission to determine the name of file C:\foo.txt". Great. Thanks for letting me know. What is stopping said assembly from catching the exception and interrogating its message to get the file name? Nothing. We fixed that before we shipped.

That's a direct problem. An indirect problem would be a problem I implemented in LoadPicture, in VBScript. It gave a different error message depending upon whether the incorrect argument is a directory, a file that isn't a picture, or a file that doesn't exist. Which means you could use it as a very slow disk browser! By trying a whole bunch of different things you could gradually build up a picture of what files and directories were on someone's hard disk. Exceptions should be designed so that if they are handled by untrustworthy code, that code learns none of the user's private information from whatever they did to cause the exception. (LoadPicture now gives much less helpful error messages.)

  • Security and resource management error: Handlers which do not clean up resources are resource leaks waiting to happen. Resource leaks can be used as denial-of-service attacks by hostile partial trust code which deliberately creates exceptions-producing situations.

  • Robustness error: Handlers must assume that program state is messed up unless handling a specific exogenous exception. This is particularly true of finally blocks. When you're handling an unexpected exception, it is entirely possible and even likely that something is deeply messed up in your program. You have no idea if any of your subsystems are working, and if they are, whether calling them will make the situation better or worse. Concentrate on logging the error and saving user data if possible and shut down as cleanly as you can. Assume that nothing works right.

  • Security error: temporary global state mutations that have security impacts need to be undone before any code that might be hostile can run. Hostile code can run before finally blocks run! See my article on this for details:

http://blogs.msdn.com/ericlippert/archive/2004/09/01/224064.aspx

Eric Lippert
+7  A: 

Nobody is talking about seeing empty catch blocks like these....

 try{  
      //do something
    }
catch(SQLException sqex){  
        // do nothing  
    }

Also never use Exception handling for creating alternate method flows...

 try{  
     //do something  

 }catch(SQLException sqex){  

     //do something else  
 }
CodeToGlory
+1 for not using exceptions for control flow.
Anna Lear
A: 

Using exceptions for normal flow control. Exceptions should exceptional. If it's a good / expected operation, use return values, etc.

Donnie
+1  A: 

Failing to catch possible exceptions inside a catch handler. This can cause the wrong exception to be propagated upwards.

For example:

try
{
    DoImportantWork();
}
catch
{
    Cleanup();        
    throw;
}

What happens if Cleanup() throws an exception? You don't want to see an Exception pointing to the Cleanup() method in this catch handler. You want the original error. You could try to log the cleanup error, but even your logging code needs exception handling to avoid throwing exceptions from it.

try
{
    DoImportantWork();
}
catch
{
    try
    {
        Cleanup();        
    }
    catch
    {
        // We did our best to clean up, and even that failed.
        // If you try to log this error, the logging may throw yet another Exception.
    }
    throw;
}
Paul Williams
-1 for the empty `catch` clause.
John Saunders
+1: Good point. There is nothing inherently wrong with an empty catch...just make sure you understand the consequences. For example, how would you implement IDisposable.Dispose without it propagating exceptions? Sometimes you just have to punt, especially when your logging component breaks down.
Brian Gideon
@Brian: Exactly. I've even seen Debug.WriteLine throw an exception! At some point, you just have to give up.
Paul Williams