Hat tip to Stephen Toub for discussing this with me and helping to describe the solution.
At my CodeMash precompiler, I mentioned how the C# compiler ensures that methods marked with the ‘async’ keyword that contain ‘await’ expressions never throw synchronous exceptions. Instead, those methods will return a Task (or Task<T>) that will be placed in the faulted state if the method throws an exception. The compiler does the work to add the appropriate try/catch clauses to your methods, and translates any exceptions thrown during your method’s execution into returned faulted task.
Jon Skeet was concerned by this strategy. He felt that API designers would prefer throwing synchronous exceptions for obvious programmer errors (things like ArgumentNullException and so on). I mentioned that the language specification defines that async methods will returned faulted tasks; they will not throw synchronous exceptions.
Of course, there is a way around this. You have to separate your public async APIs into two pieces: A public synchronous API that does parameter validation and state validation. This synchronous method then calls an internal async method that does the asynchronous work.
As an example, consider this (rather contrived) async method:
public async Task<string> FizzBuzzAsync(int val) { if (val <= 0) throw new ArgumentException("We can't fizzbuzz negative numbers, or 0"); await Task.Delay(250); var rVal = string.Empty; if (val % 3 == 0) rVal += "Fizz"; if (val % 5 == 0) rVal += "Buzz"; if (string.IsNullOrWhiteSpace(rVal)) rVal = val.ToString(); return rVal; }
Calling this method with a negative number is a programming error. We’d like to have that condition throw a synchronous exception. We can achieve this by separating the method into two parts. The first part is a synchronous method that performs the parameter validation and state validation. The second part is an internal method that performs the asynchronous work. The first method will throw exceptions synchronously. The second will report errors using a faulted task.
public Task<string> FizzBuzzAsync(int val) { if (val <= 0) throw new ArgumentException("We can't fizzbuzz negative numbers, or 0"); return FizzBuzzAsyncImpl(val); } private static async Task<string> FizzBuzzAsyncImpl(int val) { await Task.Delay(250); var rVal = string.Empty; if (val % 3 == 0) rVal += "Fizz"; if (val % 5 == 0) rVal += "Buzz"; if (string.IsNullOrWhiteSpace(rVal)) rVal = val.ToString(); return rVal; }
That ensures that your public async methods conform to both important rules.
First, TAP (Task Asynchronous Pattern) methods do not allow synchronous runtime exceptions. They must return errors by returning a faulted task.
Second, to make it easier for callers to detect and correct programming errors, simple programming errors will throw synchronous exceptions.

I don’t get it.
When I run this code: var result = await FizzBuzzAsync(0);
I do get a ArgumentException, what am I missing?
Hi there, great idea to have the exception thrown synchronously.
Splitting it up into two methods however seems to defeat the purpose of having the async keyword which automatically creates closures in the compiler so that the user code can stay intact and not require multiple methods to do async programming.
Here’s an improvement that merges both these ideas together using async lambdas. This way we get the benefit of cohesion while still getting the synchronous argument checking.
public Task FizzBuzzAsync5( int val ){
if( val <= 0 )
throw new ArgumentException( "We can't fizzbuzz negative numbers, or 0" );
return new Func<Task>( async () => {
await Task.Delay( 2500 );
var rVal = string.Empty;
if( val%3 == 0 )
rVal += "Fizz";
if( val%5 == 0 )
rVal += "Buzz";
if( string.IsNullOrWhiteSpace( rVal ))
rVal = val.ToString();
return rVal;
})();
}
Way to inspire!
@Phil: The code above awaits. Therefore, it will unpackage the faulted task and throw the exception.
If you separate it into two statements:
var t = FizzBuzzAsync(0);
var result = await t;
You’ll see that the exception is thrown on the second statement (await), not the first (the method call).
@Marcel, that’s a great idea. If you’re separating code for exceptions only, that’s a good way to express it. Often, I’ve also found that I may be caching tasks to return already retrieved results. Then, the code might look differently.
I think it’s interesting to compare this with the other compiler-built state machine in C#: iterator blocks. They have exactly the same problem and the solution is also the same.
Jon Skeet mentions this version of the problem in his EduLinq series.