locked
Workflow Foundation 4.0 Strange Behaviour: Workflow aborted after exception in Finally section of nested TryCatch block. RRS feed

  • Question

  • So some workflows in our system would suddenly die when an exception occurred, bypassing all of our error handling logic in our workflow. I was able to distill this into a very simple sample that shows this issue clearly. 

    Basically, if an exception is thrown in a TryCatch, then rethrown, and another exception is thrown in the Finally block, AND this TryCatch is wrapped in another TryCatch, the workflow would suddenly die with a workflow aborted exception.

    Below is the sample code showing this issue. The "innerTryCatch" throws an exception in the TryBlock, rethrows in the Catch Block, then throws an exception in the Finally block. When this activity is run alone, everything works as expected. When this activity is then wrapped in another TryCatch, outerTryCatch, then the workflow gets aborted.

    var innerTryCatch = new TryCatch
    {
        Try = new Throw
        {
            Exception = new InArgument<Exception>(c => new ApplicationException("Exception in inner TryCatch"))
        },
        Catches = 
        { 
            new Catch<Exception> 
            { 
                Action = new ActivityAction<Exception> {  Handler = new Rethrow() }
            } 
        },
        Finally = new Throw
        {
            Exception = new InArgument<Exception>(c => new ApplicationException("Exception in inner TryCatch-Finally"))
        }
    };
    
    var outerTryCatch = new TryCatch
    {
        Try = innerTryCatch,
        Catches = 
            { 
                new Catch<Exception> 
                { 
                    Action = new ActivityAction<Exception> 
                    { 
                        Handler = new WriteLine { Text = "!!!Exception caught in outer TryCatch" } 
                    }
                } 
            }
    };
    
    try { WorkflowInvoker.Invoke(innerTryCatch); }
    catch (Exception ex) { Console.WriteLine("Inner TryCatch: " + ex); }
    
    Console.WriteLine("==================================================");
    
    try { WorkflowInvoker.Invoke(outerTryCatch); }
    catch (Exception ex) { Console.WriteLine("Outer TryCatch: " + ex); }

    The console output:

    Inner TryCatch: System.ApplicationException: Exception in inner TryCatch
       at System.Activities.WorkflowApplication.Invoke(Activity activity, IDictionary`2 inputs, WorkflowInstanceExtensionManager extensions, TimeSpan timeout)
       at System.Activities.WorkflowInvoker.Invoke(Activity workflow, TimeSpan timeout, WorkflowInstanceExtensionManager extensions)
       at System.Activities.WorkflowInvoker.Invoke(Activity workflow)
       at WorkflowTryCatchBug.Program.Main(String[] args) Program.cs:line 49
    ==================================================
    Outer TryCatch: System.Activities.WorkflowApplicationAbortedException: The workflow has been aborted. ---> System.InvalidOperationException: The activity 'TryCatch' with ID 2 threw or propagated an exception while being canceled. ---> System.ApplicationException: Exception in inner TryCatch-Finally
       at System.Activities.Statements.Throw.Execute(CodeActivityContext context)
       at System.Activities.CodeActivity.InternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager)
       at System.Activities.Runtime.ActivityExecutor.ExecuteActivityWorkItem.ExecuteBody(ActivityExecutor executor, BookmarkManager bookmarkManager, Location resultLocation)
       --- End of inner exception stack trace ---
       --- End of inner exception stack trace ---
       at System.Activities.WorkflowApplication.Invoke(Activity activity, IDictionary`2 inputs, WorkflowInstanceExtensionManager extensions, TimeSpan timeout)
       at System.Activities.WorkflowInvoker.Invoke(Activity workflow, TimeSpan timeout, WorkflowInstanceExtensionManager extensions)
       at System.Activities.WorkflowInvoker.Invoke(Activity workflow)
       at WorkflowTryCatchBug.Program.Main(String[] args) in Program.cs:line 54

    Is this expected behaviour? ie - is this a bug or a feature?

    What should I do to let the workflow continue executing without dying? Always wrap the finally block in a TryCatch? But what if I want to throw an exception in the Finally?



    • Edited by Mas 2112 Thursday, February 6, 2014 2:43 PM
    Tuesday, January 28, 2014 10:57 AM

Answers

  • This is by design.

    When the TryCatch activity encounters an excepiton during the Try processing, it cancels the activity that generated the exception, if it not already completed.

    When you wrap the innerTryCatch with the outerTryCatch, the innerTryCatch generates the exception out of its Catch processing, which propagates to the outerTryCatch, and the innerTryCatch is not complete, so it is scheduled to be canceled. But its Finally processing happens, which also throws, so innerTryCatch tries to propagate THAT exception. But innerTryCatch is already marked as being canceled, and propagating exceptions is not allowed in that scenario, so the workflow is aborted.

    You should avoid throwing out of Finally "blocks".

    • Marked as answer by Mas 2112 Friday, February 21, 2014 3:28 PM
    Thursday, February 20, 2014 3:51 AM

All replies

  • If you use innerTryCatch only, the inner exception throw from Catch will escape to the root of the workflow. And finally block will not be executed. Therefore there is only one inner exception which is handled c# catch code. See below: 

    http://social.msdn.microsoft.com/Forums/vstudio/en-US/d54bd44a-24b7-4b0b-aa3c-f4553e00c820/finally-never-executed?forum=wfprerelease

    If you are using outerTryCatch, the exception in finally block will also be thrown as well as the inner exception. The Outer TryCatch may not be able to handle such situation.

    Friday, January 31, 2014 10:07 AM
  • So what is the correct way to handle this situation to let the workflow continue working? Wrapping this code with more try-catches will not prevent the workflow from dying.

    Friday, January 31, 2014 12:27 PM
  • Actually we need to avoid throwing exceptions from both of exception handler hand finally block.
    Thursday, February 6, 2014 2:41 AM
  • But is this behaviour a feature or a bug? To me, the workflow aborting under this situation is equivalent to the entire process crashing if an exception occurs in the catch and the finally of a try/catch/finally statement in C# (I did test this, the outer try catch can handle this situation).


    • Edited by Mas 2112 Thursday, February 6, 2014 2:43 PM
    Thursday, February 6, 2014 2:42 PM
  • This is by design.

    When the TryCatch activity encounters an excepiton during the Try processing, it cancels the activity that generated the exception, if it not already completed.

    When you wrap the innerTryCatch with the outerTryCatch, the innerTryCatch generates the exception out of its Catch processing, which propagates to the outerTryCatch, and the innerTryCatch is not complete, so it is scheduled to be canceled. But its Finally processing happens, which also throws, so innerTryCatch tries to propagate THAT exception. But innerTryCatch is already marked as being canceled, and propagating exceptions is not allowed in that scenario, so the workflow is aborted.

    You should avoid throwing out of Finally "blocks".

    • Marked as answer by Mas 2112 Friday, February 21, 2014 3:28 PM
    Thursday, February 20, 2014 3:51 AM