WCF
Async Programming
IAsyncResult
Exception Handling
Callbacks

How to throw an exception from callback in WCF Async using IAsyncResult

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

When old WCF code uses the Begin and End asynchronous pattern, exception flow works differently from normal synchronous method calls. The callback is a completion notification, not an extension of the original caller stack, so the safest place to observe errors is usually the matching End... method.

Why throw in the Callback Is Usually the Wrong Goal

A common misunderstanding is thinking that this code will “send” the exception back to the code that called BeginDoWork:

csharp
1proxy.BeginDoWork(ar =>
2{
3    throw new InvalidOperationException("Failed");
4}, null);

It does throw, but not in the way most callers expect. The callback runs later, often on a different thread-pool thread. That means the exception is no longer inside the synchronous flow that originally started the request. Depending on your host and runtime settings, an unhandled exception there may crash the process, get swallowed by framework code, or show up in logs without giving the original caller a clean way to react.

In the Asynchronous Programming Model, the real contract is:

  • 'BeginOperation(...) starts the work.'
  • 'EndOperation(IAsyncResult result) completes it.'
  • operation failures are typically rethrown when EndOperation is called.

So the practical answer is not “throw from the callback,” but “call End... in the callback and handle the failure there.”

The Correct WCF Pattern

A minimal client-side pattern looks like this:

csharp
1proxy.BeginDoWork(ar =>
2{
3    try
4    {
5        string result = proxy.EndDoWork(ar);
6        Console.WriteLine("Success: " + result);
7    }
8    catch (TimeoutException ex)
9    {
10        Console.WriteLine("Timeout: " + ex.Message);
11    }
12    catch (CommunicationException ex)
13    {
14        Console.WriteLine("Communication error: " + ex.Message);
15    }
16    catch (Exception ex)
17    {
18        Console.WriteLine("Unexpected failure: " + ex.Message);
19    }
20}, null);

This pattern matters for two reasons. First, EndDoWork is the place where WCF surfaces many service or transport failures. Second, calling EndDoWork completes the asynchronous contract correctly. Skipping it can leave you with hidden failures and incomplete cleanup.

If the service threw a fault, the client may see a FaultException, CommunicationException, or TimeoutException instead of the raw server exception type. That is normal. WCF serializes failures according to its service contract and channel rules.

Propagating the Error to Other Code

Sometimes the callback should not fully handle the failure. For example, a UI layer or a higher-level service may need to know whether the operation failed. In that case, catch inside the callback and forward the exception through a mechanism designed for asynchronous composition.

A clean option is TaskCompletionSource:

csharp
1using System;
2using System.ServiceModel;
3using System.Threading.Tasks;
4
5public Task<string> DoWorkAsync(MyServiceClient proxy)
6{
7    var tcs = new TaskCompletionSource<string>();
8
9    proxy.BeginDoWork(ar =>
10    {
11        try
12        {
13            string value = proxy.EndDoWork(ar);
14            tcs.SetResult(value);
15        }
16        catch (Exception ex)
17        {
18            tcs.SetException(ex);
19        }
20    }, null);
21
22    return tcs.Task;
23}

Now the rest of your code can use familiar async error handling:

csharp
1try
2{
3    string result = await DoWorkAsync(proxy);
4    Console.WriteLine(result);
5}
6catch (Exception ex)
7{
8    Console.WriteLine("Observed by caller: " + ex.Message);
9}

This does not change where the exception originates. It just packages the asynchronous outcome in a way the caller can consume predictably.

When You Need Custom State

The AsyncState parameter can be used to pass a context object into the callback. That is useful when you need correlation data, UI state, or a completion handler.

csharp
1public sealed class RequestState
2{
3    public string OperationName { get; set; }
4}
5
6var state = new RequestState { OperationName = "DoWork" };
7
8proxy.BeginDoWork(ar =>
9{
10    var requestState = (RequestState)ar.AsyncState;
11
12    try
13    {
14        string result = proxy.EndDoWork(ar);
15        Console.WriteLine(requestState.OperationName + " succeeded: " + result);
16    }
17    catch (Exception ex)
18    {
19        Console.WriteLine(requestState.OperationName + " failed: " + ex.Message);
20    }
21}, state);

This is still not synchronous exception propagation, but it gives you a reliable way to associate failures with the operation that caused them.

Common Pitfalls

The most common mistake is not calling End... at all. In APM-style WCF code, that is where completion happens and where many errors are rethrown.

Another mistake is assuming throw inside the callback can be caught by the code that already returned from Begin.... Once control has returned to the caller, that synchronous catch block is gone.

Some code catches the exception in the callback and silently ignores it. That avoids a crash, but it also hides faults and makes diagnosis much harder.

A final problem appears when old WCF code mixes APM and modern async code without an adapter. Wrapping the operation in a Task usually makes the exception path much easier to understand and test.

Summary

  • In WCF APM, call End... inside the callback to observe the real operation result.
  • Throwing directly from the callback does not behave like synchronous exception propagation.
  • WCF may surface failures as FaultException, CommunicationException, or TimeoutException.
  • Use TaskCompletionSource or another explicit async mechanism when higher layers need the failure.
  • Treat the callback as an async boundary, not as a normal caller stack frame.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.