none
Try Block Inside Catch Block RRS feed

  • Question

  • Many people employ nested try-catch blocks, which almost always refers to a try-catch inside of a try block.  But what about a try-catch inside a catch block?  The CLR allows it - but I don't like it.

    A typical example I've seen is for developers to put logging statements in every catch block, where the logging call itself wraps its own try-catch (in case the logger throws an exception).

    My feeling is this: by the time your application has experienced an exception, followed instantly by your logger also throwing an exception - the fat lady is singing.  Its time to just let the exception through rather than attempting to catch it (likely resulting in a massive stack unwind and possibly termination of your program). 

    In other words, I don't think that try-catch blocks should be used inside of catch blocks.  Do others feel the same way?
    -Brent Arias
    Saturday, January 10, 2009 1:30 AM

Answers

  • This is bound to happen anyway.  When you call some kind of logging function, the code is very likely to pass through some kind of try/finally block inside the logging code.  Since you already paid the big penalty of catching an exception, don't worry about paying for it more than once.  In other words, the first exception makes your code ~100 times slower, the 2nd exception only makes it twice as slow.  If it actually would be raised that is.  Try is very cheap, only catch is expensive.  Nobody can afford a crashed program without diagnostics.
    Hans Passant.
    • Marked as answer by Brent Arias Thursday, January 15, 2009 10:09 PM
    • Unmarked as answer by Brent Arias Thursday, January 15, 2009 10:09 PM
    • Marked as answer by Zhi-Xin Ye Friday, January 16, 2009 6:03 AM
    Saturday, January 10, 2009 11:12 AM
    Moderator

All replies

  • Often, the catch block needs to perform cleanup work.  Sometimes the cleanup work itself fails.  Or it performs logging where if the logging fails with an exception you might want to ignore it.  It really is not possible to make any blanket statements here; the catch block needs to be written so that the program performs according to its requirements.

    Saturday, January 10, 2009 2:26 AM
  • Well Mystagogue. Thanks for sharing your idea on the try...catch blocks.

    Let me explain a scenario. Assume that you are inserting a bunch of records into the database using bulkcopy object. if there exist some of the records among the set of records which you are trying to bulk insert, already in the database, and you want to insert only the missing records in the same case, definetly you should use try...catch in the catch of bulk copy try, to insert only the missing records.

    In the scenario specified above, I used try... catch within catch block. It is working fine and I have no problems found till now. For the past 1 year the server is running efficiently.

    Let us wait for some more feedback on this!
    Ganesh Kumar
    • Proposed as answer by technicalganesh Saturday, January 10, 2009 2:38 AM
    • Unproposed as answer by Brent Arias Thursday, January 15, 2009 10:09 PM
    Saturday, January 10, 2009 2:34 AM
  • For the particular example of inserting many records into a database, I suspect that handling each collision as an exception in a catch block...is workable but very slow.  I would be inclined to perform an insert from an outer join so that no collisions are possible.  It should have much higher performance, and not involve try blocks within catch blocks (which you already know I find alarming). :)
    -Brent Arias
    Saturday, January 10, 2009 2:57 AM
  • "In other words, I don't think that try-catch blocks should be used inside of catch blocks.  Do others feel the same way?"

    Not really, no.  To be blunt, this is nonsense.  Logging is an I/O operation, of course it can throw exceptions, and I/O exceptions are among those few types that can sensibly be caught and handled.  Doesn't matter whether it's in a catch block or not.
    Saturday, January 10, 2009 8:45 AM
  • This is bound to happen anyway.  When you call some kind of logging function, the code is very likely to pass through some kind of try/finally block inside the logging code.  Since you already paid the big penalty of catching an exception, don't worry about paying for it more than once.  In other words, the first exception makes your code ~100 times slower, the 2nd exception only makes it twice as slow.  If it actually would be raised that is.  Try is very cheap, only catch is expensive.  Nobody can afford a crashed program without diagnostics.
    Hans Passant.
    • Marked as answer by Brent Arias Thursday, January 15, 2009 10:09 PM
    • Unmarked as answer by Brent Arias Thursday, January 15, 2009 10:09 PM
    • Marked as answer by Zhi-Xin Ye Friday, January 16, 2009 6:03 AM
    Saturday, January 10, 2009 11:12 AM
    Moderator
  •  Logging is great evil. It gives developers permission not to be concerned about generating good error messages. In encourages lazy handling of errors. And it produces bad diagnostics, that are punishingly difficult to work with.

    One of the essential tools for generating good error messages is chained errors. e.g.:

     
     
        catch (Exception e)  
        {  
            throw new Exception(  
                String.Format(  
                    "Unabled to read the migration configuration file "{0}" for server {0}. {1}",  
                     filename,
                     server,  
                     e.Message  
                    )  
                );  
        }  
     
       }  
     
     

    This style of error handling produces error messages like the following:

                 "Unable to read the migration configuration file \\QSMNASW01\Migrator\Configuration\QMSDC01.Settings" for server QSMDC01. Permission denied."
    or
                 "Unable to read the migration configuration file \\QSMNASW01\Migrator\Configuration\QMSDC01.Settings" for server QSMDC01. Server not reachable: QSMNASW01."
    or
                 "Unable to read the migration configuration file \\QSMNASW01\Migrator\Configuration\QMSDC01.Settings" for server QSMDC01. File not found."
    or 
                 "Unable to read the migration configuration file \\QSMNASW01\Migrator\Configuration\QMSDC01.Settings" for server QSMDC01. Xml syntax error on line 201.
    or
                 "Unable to read the migration configuration file \\QSMNASW01\Migrator\Configuration\QMSDC01.Settings" for server QSMDC01. Syntax error in section "Accounts".

    or even the nightmare cases:
                 "Unable to read the migration configuration file \\QSMNASW01\Migrator\Configuration\QMSDC01.Settings" for server QSMDC01. Error processing account 
                  configuration for user QSM\\rerdavies: Unhandled exception."

    The important point: these messages provide sufficient information that an ordinary user might be able to resolve the problem themselves without calling up tech support. The are enabling. And they provide just as much contextual information as log files do.

    One can genrate megabytes of log files that are intended to provide enough context to reconstruct what was happening when an error occurred. Or one can determinedly and systematically ensure that any error that occurs provides an error message with enough context to figure out what was going on when an error occurred. The latter requires actual thought on programmers' part. But that's a good thing, not a bad thing. If error can happen, then programmers should be giving thought as to how to handle them, even in exception-based error reporting systems.

    I deal with escalated support cases all the time where a review of log files indicate that 60 or 70 errors occurred while performing a task. Of course those 60 or 70 errors occur in 4.5GB of log files. And it takes hours to reconstruct the sorry trail of what went wrong, so one can forgive the poor users who call us up trying to figure out what the heck did go wrong.  And despite the 4.5GB of log files, there's invariably not enough information in the log files to determine unambigously what did go wrong. So we send them a patch with even MORE logging.

    Imagine how differet it would be if, instead of gigabytes of unreadable ____, users got a single page of error messages that actually explained what when wrong, when, and (usually) why. I've worked with products like that too.

    Sure. Error messages may get a little baroque when error handling gets excessively nested. But it surprising how lucid error messages written in this style are in almost all cases.

    The .net InnerException mechanism also provides a good solution for providing meaningful error messagesprovided error handlers actually display the inner exception (which they should, in my opinion).

    Honestly, I still havent' formed an opinion as to whether it's better to provide error messages of the form "Sentence. Sentence. Sentence.", using String.Format, or "Paragraph. Paragraph. Paragraph." using inner exceptions. I lean toward the former, because the error messages are generally much kinder and gentler, and REQUIRE MORE THOUGHT ON THE PROGRAMMER'S PART in order to present complete error context. But the inner exception method also produces good error messages, and may actually be required to fully preserve error messages generated by refelection, or by ADO.NET. Most of my current code uses both strategies (String.Format as a general rule, but inner exceptions are handled, and used, and fully reported.



    Saturday, January 10, 2009 5:50 PM
  • nobugz - Performance is an issue, but much more important is "sanity".  Again: if you are already handling an exception, and then your logger also throws an exception (e.g. it is out of logging space), then why bother to continue your program?  Everything that goes wrong with your program from that point forward will not have any logging (because you are out of space), and everything the program does will be slower.  In short, your program is now crippled (I hope it isn't supposed to be a high performance system!!!).  Wouldn't it be far better to simply let the program terminate?  Put differently, what good is it to keep a program alive and running, when it can't even log anymore?  Any further errors the program produces will not be logged!!! That sounds very bad to me.

    Let me ask a different question: what condition(s) must be met before you would agree that it is time to let a program terminate? My answer is: when the program is no longer fully functioning.  I think your answer is: when there is a stack overflow.  In other words, I'm guessing you'd only allow the program to terminate when a nearly un-catchable exception is thrown.  Yes?
    -Brent Arias
    Monday, January 12, 2009 7:44 PM