title: OperationCanceledException VS TaskCanceledException when task is canceled
body: <p>The following code creates a task which is being canceled. <code>await</code> expression (case 1) throws <code>System.OperationCanceledException</code> while synchronous <code>Wait()</code> (case 2) throws <code>System.Threading.Tasks.TaskCanceledException</code> (wrapped in <code>System.AggregateException</code>). </p>
  
  <pre><code>using System;
  using System.Threading;
  using System.Threading.Tasks;
  
  public class Program
  {
      public static void Main()
      {
          Program.MainAsync().Wait();
      }
  
      private static async Task MainAsync()
      {
          using(var cancellationTokenSource = new CancellationTokenSource())
          {
              var token = cancellationTokenSource.Token;
              const int cancelationCheckTimeout = 100;
  
              var task = Task.Run(
                  async () =&gt; 
                  {
                      for (var i = 0; i &lt; 100; i++)
                      {
                          token.ThrowIfCancellationRequested();
                          Console.Write(".");
                          await Task.Delay(cancelationCheckTimeout);  
                      }
                  }, 
                  cancellationTokenSource.Token
              );
  
              var cancelationDelay = 10 * cancelationCheckTimeout;
              cancellationTokenSource.CancelAfter(cancelationDelay);
  
              try
              {
                  await task; // (1)
                  //task.Wait(); // (2) 
              }
              catch(Exception ex)
              {
                  Console.WriteLine(ex.ToString());
                  Console.WriteLine($"Task.IsCanceled: {task.IsCanceled}");
                  Console.WriteLine($"Task.IsFaulted: {task.IsFaulted}");
                  Console.WriteLine($"Task.Exception: {((task.Exception == null) ? "null" : task.Exception.ToString())}");
              }
          }
      }
  }
  </code></pre>
  
  <p>Case 1 output:</p>
  
  <pre><code>..........System.OperationCanceledException: The operation was canceled.
     at System.Threading.CancellationToken.ThrowIfCancellationRequested()
     at Program.&lt;&gt;c__DisplayClass1_0.&lt;&lt;MainAsync&gt;b__0&gt;d.MoveNext()
  --- End of stack trace from previous location where exception was thrown ---
     at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
     at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
     at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
     at Program.&lt;MainAsync&gt;d__1.MoveNext()
  Task.IsCanceled: True
  Task.IsFaulted: False
  Task.Exception: null
  </code></pre>
  
  <p>Case 2 output:</p>
  
  <pre><code>..........System.AggregateException: One or more errors occurred. ---&gt; System.Threading.Tasks.TaskCanceledException: A task was canceled.
     --- End of inner exception stack trace ---
     at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
     at System.Threading.Tasks.Task.Wait()
     at Program.&lt;MainAsync&gt;d__1.MoveNext()
  ---&gt; (Inner Exception #0) System.Threading.Tasks.TaskCanceledException: A task was canceled.&lt;---
  
  Task.IsCanceled: True
  Task.IsFaulted: False
  Task.Exception: null
  </code></pre>
  
  <p><strong>Why <code>System.AggregateException</code> in the 2nd case doesn't contain <code>System.OperationCanceledException</code> as an inner exception?</strong> </p>
  
  <p>I know that <code>ThrowIfCancellationRequested()</code> throws <code>OperationCanceledException</code> and we can see that in both cases <code>Task</code> gets to canceled (not faulty) state.</p>
  
  <p>This puzzles me because canceling a method from .NET API produces consistent behaviour in both cases - canceled task throws only <code>TaskCanceledException</code>:</p>
  
  <pre><code>using System;
  using System.Threading;
  using System.Threading.Tasks;
  
  public class Program
  {
      public static void Main()
      {
          Program.MainAsync().Wait();
      }
  
      private static async Task MainAsync()
      {
          using(var cancellationTokenSource = new CancellationTokenSource())
          {
              var token = cancellationTokenSource.Token;
  
              var task = Task.Delay(1000, token);
              cancellationTokenSource.CancelAfter(100);
  
              try
              {
                  await task; // (1)
                  //task.Wait(); // (2)
              }
              catch(Exception ex)
              {
                  Console.WriteLine(ex.ToString());
                  Console.WriteLine($"Task.IsCanceled: {task.IsCanceled}");
                  Console.WriteLine($"Task.IsFaulted: {task.IsFaulted}");
                  Console.WriteLine($"Task.Exception: {((task.Exception == null) ? "null" : task.Exception.ToString())}");
              }
          }
      }
  }
  </code></pre>
  
  <p>Case 1 output:</p>
  
  <pre><code>System.Threading.Tasks.TaskCanceledException: A task was canceled.
     at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
     at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
     at Program.&lt;MainAsync&gt;d__1.MoveNext()
  Task.IsCanceled: True
  Task.IsFaulted: False
  Task.Exception: null
  </code></pre>
  
  <p>Case 2 output:</p>
  
  <pre><code>System.AggregateException: One or more errors occurred. ---&gt; System.Threading.Tasks.TaskCanceledException: A task was canceled.
     --- End of inner exception stack trace ---
     at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
     at System.Threading.Tasks.Task.Wait()
     at Program.&lt;MainAsync&gt;d__1.MoveNext()
  ---&gt; (Inner Exception #0) System.Threading.Tasks.TaskCanceledException: A task was canceled.&lt;---
  
  Task.IsCanceled: True
  Task.IsFaulted: False
  Task.Exception: null
  </code></pre>