title: C# TPL Task propagate exceptions - Multi-level task
body: <p>The <code>Run()</code> method (first block of code) makes a call to <code>GetImpairedNodesFromCASpectrumAsync()</code>, which in turn makes a call to <code>GetRoutersOn3GBackupNodeStatusesAsync()</code>.</p>
  
  <p>Currently, if any of the tasks in <code>GetRoutersOn3GBackupNodeStatusesAsync()</code> fail (due to an exception), I get a very generic Exception in the <code>Run()</code> method that says the task was cancelled.</p>
  
  <p>How can I ensure that any Tasks in my call stack end up returning the original Exception back to the run method, so that I can handle it there?</p>
  
  <pre><code>public override void Run(ref DevOpsScheduleEntryEventCollection events)
  {
      // I want to be able to catch any exceptions thrown from tasks in GetRoutersOn3GBackupNodeStatusesAsync()
  
      Task&lt;NetworkDeviceNodeStatus[]&gt; CasImapairedNodesTask = 
                  CasOperations.GetImpairedNodesFromCASpectrumAsync();
      Task.WaitAll(CasImapairedNodesTask);
      NetworkDeviceNodeStatus[] CasImpairedNodes = CasImapairedNodesTask.Result.ToArray();
  }
  
  internal virtual async Task&lt;NetworkDeviceNodeStatus[]&gt; GetImpairedNodesFromCASpectrumAsync()
  {
  #if DEBUG
      Debug.WriteLine("Entering GetNodesInCriticalCondition()");
      Stopwatch sw = new Stopwatch();
      sw.Start();
  #endif
  
      // Execute both tasks.  Throw an Exception if any errors.
      try {
          var nodesWithCircuitsDown = new List&lt;NetworkDeviceNodeStatus&gt;();
  
          Task&lt;NetworkDeviceNodeStatus[]&gt; getAlarmingRoutersStatusesTask = null;
          Task&lt;NetworkDeviceNodeStatus[]&gt; getActive3GRoutersStatusesTask = null;
  
          getAlarmingRoutersStatusesTask = GetAlarmingRouterNodeStatusesAsync();
          getActive3GRoutersStatusesTask = GetRoutersOn3GBackupNodeStatusesAsync();
  
          await getAlarmingRoutersStatusesTask;
          await getActive3GRoutersStatusesTask;
  
          var threeGNodeStatuses = new List&lt;NetworkDeviceNodeStatus&gt;();
          var offlineNodeStatuses = new List&lt;NetworkDeviceNodeStatus&gt;();
  
  
          // Check if any nodes were hard down, but quickly came up on 3G
          foreach (var status in getAlarmingRoutersStatusesTask.Result) {
              var threeGStatus = getActive3GRoutersStatusesTask.Result.
                  FirstOrDefault(x =&gt; x.DeviceRetrievalId == status.DeviceRetrievalId);
  
              if (threeGStatus == null) {
                  offlineNodeStatuses.Add(status);
              }
          }
  
          foreach (var status in getActive3GRoutersStatusesTask.Result) {
              threeGNodeStatuses.Add(status);
          }
  
          nodesWithCircuitsDown.AddRange(threeGNodeStatuses);
          nodesWithCircuitsDown.AddRange(offlineNodeStatuses);
  
          Trace.TraceInformation("{0} nodes with main data circuit down.", nodesWithCircuitsDown.Count);
  #if DEBUG
          sw.Stop();
          Debug.WriteLine("Leaving GetNodesInCriticalCondition(). [" + sw.Elapsed.TotalSeconds + "]");
  #endif
          return nodesWithCircuitsDown.ToArray();
      } catch (AggregateException ae) {
          StringBuilder sb = new StringBuilder();
          foreach (var e in ae.Flatten().InnerExceptions) {
              sb.Append(e.Message + "\n");
          }
          throw new Exception("One of more errors occured while retrieving impaired nodes.\n" + sb.ToString());
      }
  }
  
  virtual internal async Task&lt;NetworkDeviceNodeStatus[]&gt; GetRoutersOn3GBackupNodeStatusesAsync()
  {
      List&lt;Branch3GInfo&gt; branchActive3GInfos = new List&lt;Branch3GInfo&gt;();
  
      var nodeStatuses = new List&lt;NetworkDeviceNodeStatus&gt;();
      Task&lt;Branch3GInfo[]&gt; getActive3GRoutersTask = GetNodesOn3GBackupAsyncInternal();
      NetworkDeviceNodeStatus[] deviceStatuses = new NetworkDeviceNodeStatus[0];
  
      Task getBasicInfoTasks = await getActive3GRoutersTask.ContinueWith(async x =&gt;
      {
          branchActive3GInfos = x.Result.Where(y =&gt; y.Status == Branch3GInfo.Branch3GStatus.Active).ToList();
          Trace.TraceInformation("Found " + x.Result.Count() + " CAS Nodes on 3G backup.");
  
          foreach (var branchActive3GInfo in branchActive3GInfos) {
              await branchActive3GInfo.RouterInfo.GetBasicInfoAsync();
              Trace.TraceInformation("Retrieved ModelBasicInfo for "
                  + branchActive3GInfo.RouterInfo.GetBasicInfo());
          }
  
      }, TaskContinuationOptions.OnlyOnRanToCompletion);
  
      await getBasicInfoTasks.ContinueWith(x =&gt;
      {
          deviceStatuses = GetNetworkDeviceNodeStatuses(branchActive3GInfos.ToArray());
          return deviceStatuses;
      }, TaskContinuationOptions.OnlyOnRanToCompletion);
  
      return deviceStatuses;
  }
  </code></pre>