Search code examples
c#task-parallel-librarytpl-dataflow

Why does Try/catch in ActionBlock not catch this error sometimes?


I'm using an ActionBlock in a web crawler I'm writing.

Sometimes when I call

actionBlock.Completion.Wait();

I get the following error

One or more errors occurred.

With the inner exception being

System.Threading.Tasks.TaskCanceledException: A task was canceled.

This is the full code block below.

actionBlock = new ActionBlock<URLsToCheckObject>(URLToCheck =>
{
    try
    {
        // get more urls etc here and post below

        actionBlock.Post(new URLsToCheckObject { URLAddress = CleanURL, Host = host });
        if (actionBlock.InputCount == 0) actionBlock.Complete();
    }
    catch (Exception ex)
    {
        try
        {
            Logger.AddToDebugLog("Block 3 catch...", WebsiteToCrawl);
        }
        catch { }
    }

}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 3, CancellationToken = cancellationSource.Token });

actionBlock.Post(new URLsToCheckObject { URLAddress = WebsiteToCrawl.website, Host = host });

try
{
    Logger.AddToDebugLog("Waiting on action block..", WebsiteToCrawl);
    actionBlock.Completion.Wait();
}
catch (Exception ex)
{
    try
    {
        Logger.AddToDebugLog("Block 4 catch..." + ex.Message, WebsiteToCrawl);
    }
    catch { }

    try
    {
        Logger.AddToDebugLog("Block 4 catch..." + ex.InnerException, WebsiteToCrawl);
    }
    catch { }

    try
    {
        Logger.AddToDebugLog("Block 4 catch...", WebsiteToCrawl);
    }
    catch { }
}

Why would the try/catch that wraps the entire contents of the ActionBlock not catch this exception?


Solution

  • The exception comes normally from:

    cancellationToken.ThrowIfCancellationRequested();
    

    since you did not uses the CancellationToken inside your lambda, but it is used as a parameter for:

    new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 3, CancellationToken = cancellationSource.Token }
    

    the TaskCanceledException does not come from your lambda (but probably from inside of ExecutionDataflowBlockOptions or ActionBlock) so it is outside the scope of your try/catch inside the lambda, and so it does not catch.