Friday, July 5, 2013

Cannot await in the body of a catch clause

I stumbled onto this error recently: "Cannot await in the body of a catch clause". Or as ReSharper says it "The 'await' operator cannot occur inside a catch or finally block of a try-statement, inside the block of a lock statement, or in an unsafe context".

The limitations as described by ReSharper all seem to be for different reasons.  I'm only describing the bit about the catch block, but if you're interested in why you can't await inside of a lock statement or for some interesting reading check out this StackOverflow post.

It happened in code that looked like this:

try
{
   
return await GetMinutesOnce(show);
}
catch
{
   
await RandomDelay();
   
return await GetMinutesOnce(show);
}



The idea is simple: try to do something, if it fails for any reason whatsoever, then wait for a random amount of time and try again.

After a little digging I was surprised to learn that awaiting inside of a catch block is disallowed because of a limitation in the CLR.  Normally when you're inside of a catch block in C# you can use the throw; statement, all by itself without an exception, and the CLR will re-throw the exception from where it originated with it's previous stack trace and all meta-data.

The throw; statement is a nice feature, and it works because the CLR automatically keeps track of the "ambient exception".  The problem with an await in a catch is that when control is yielded to some other place in code, another exception could be thrown which would overwrite the current ambient exception and cause your throw; statement to throw the wrong exception.

In my case I was able to fake out the compiler by doing this:

try
{
   
return await GetMinutesOnce(show);
}
catch
{
   
// await can't exist in a catch because the CLR would lose the ambient
    //  exception. We don’t need the ambient exception (i.e. we don't "throw;", 
    //  so we need to trick it out by putting retry logic after the catch
}
await RandomDelay();
return await GetMinutesOnce(show);



Not very pretty, but it seems to solve the problem.

It seems to me as if the compiler should only error when both an await and a throw statement occur together in a catch, but I guess compilers don't really work like that.  After all, which line number would it list?

Anyway, I thought it was interesting.  Feel free to post a comment if you can think of a better solution.

No comments: