Skip to content

Commit c313079

Browse files
Merge pull request #313356 from hhunter-ms/hh-319367
[Durable][UUF] Add missing instructions and isolated model
2 parents 7605f04 + 59e909b commit c313079

1 file changed

Lines changed: 99 additions & 0 deletions

File tree

articles/azure-functions/durable/durable-functions-error-handling.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ public static async Task Run(
8383
> - The exception message typically identifies which activity functions or sub-orchestrations caused the failure. To access more detailed error information, inspect the [`FailureDetails`](/dotnet/api/microsoft.durabletask.taskfailuredetails) property.
8484
> - By default, `FailureDetails` includes the **error type**, **error message**, **stack trace**, and any **nested inner exceptions** (each represented as a recursive `FailureDetails` object). To include additional exception properties in the failure output, see [Include Custom Exception Properties for FailureDetails (.NET Isolated)](#include-custom-exception-properties-for-failuredetails-net-isolated).
8585
86+
> [!IMPORTANT]
87+
> **Migration note (in-process to isolated):** In the in-process model, `FunctionFailedException.InnerException` contains the original exception object thrown by the activity, which you can cast and inspect directly. In the isolated worker model, `TaskFailedException` does **not** contain the original exception as an `InnerException`. Instead, error details are available only through the [`FailureDetails`](/dotnet/api/microsoft.durabletask.taskfailuredetails) property, which provides string-based properties (`ErrorType`, `ErrorMessage`, `StackTrace`). You can't cast or access the original exception object directly. Use [`FailureDetails.IsCausedBy<T>()`](/dotnet/api/microsoft.durabletask.taskfailuredetails.iscausedby) to check the original exception type.
88+
8689
</details>
8790
<br>
8891
<details>
@@ -438,6 +441,102 @@ If the **CreditAccount** activity fails, the orchestrator catches the exception
438441

439442
::: zone pivot="durable-functions"
440443

444+
## Errors with multiple activity calls (fan-out/fan-in)
445+
446+
# [C#](#tab/csharp)
447+
448+
When you use `Task.WhenAll` to run multiple activity calls in parallel (fan-out/fan-in pattern) and one or more activities fail, `await` throws only the first exception. To access all failures, inspect the `Exception` property on the `Task` returned by `Task.WhenAll`.
449+
450+
<details>
451+
<summary><b>Isolated worker model</b></summary>
452+
453+
```csharp
454+
var tasks = new[]
455+
{
456+
context.CallActivityAsync("Activity1", input1),
457+
context.CallActivityAsync("Activity2", input2),
458+
context.CallActivityAsync("Activity3", input3),
459+
};
460+
461+
var allTask = Task.WhenAll(tasks);
462+
try
463+
{
464+
await allTask;
465+
}
466+
catch (TaskFailedException)
467+
{
468+
// 'await' rethrows only the first exception. To inspect all failures,
469+
// check allTask.Exception, which is an AggregateException.
470+
if (allTask.Exception != null)
471+
{
472+
foreach (var inner in allTask.Exception.InnerExceptions)
473+
{
474+
if (inner is TaskFailedException taskFailed)
475+
{
476+
// Use taskFailed.FailureDetails to inspect error details
477+
var errorType = taskFailed.FailureDetails.ErrorType;
478+
var errorMessage = taskFailed.FailureDetails.ErrorMessage;
479+
}
480+
}
481+
}
482+
}
483+
```
484+
485+
</details>
486+
<br>
487+
<details>
488+
<summary><b>In-process model</b></summary>
489+
490+
```csharp
491+
var tasks = new[]
492+
{
493+
context.CallActivityAsync("Activity1", input1),
494+
context.CallActivityAsync("Activity2", input2),
495+
context.CallActivityAsync("Activity3", input3),
496+
};
497+
498+
var allTask = Task.WhenAll(tasks);
499+
try
500+
{
501+
await allTask;
502+
}
503+
catch (FunctionFailedException)
504+
{
505+
// 'await' rethrows only the first exception. To inspect all failures,
506+
// check allTask.Exception, which is an AggregateException.
507+
if (allTask.Exception != null)
508+
{
509+
foreach (var inner in allTask.Exception.InnerExceptions)
510+
{
511+
if (inner is FunctionFailedException funcFailed)
512+
{
513+
// Use funcFailed.InnerException to access the original exception
514+
}
515+
}
516+
}
517+
}
518+
```
519+
520+
</details>
521+
522+
# [JavaScript](#tab/javascript)
523+
524+
In JavaScript, when you use `context.df.Task.all` to run multiple activity calls in parallel, the first failure causes the task to complete with an error. Wrap the call in a try/catch block to handle the error.
525+
526+
# [Python](#tab/python)
527+
528+
In Python, when you use `context.task_all` to run multiple activity calls in parallel, the first failure causes the task to complete with an error. Wrap the call in a try/except block to handle the error.
529+
530+
# [PowerShell](#tab/powershell)
531+
532+
In PowerShell, use `Wait-DurableTask` with multiple tasks. If any task fails, the error is raised.
533+
534+
# [Java](#tab/java)
535+
536+
In Java, when you use `ctx.allOf` to run multiple activity calls in parallel, the first failure causes the task to complete with an error. Use a try/catch block to handle the error.
537+
538+
---
539+
441540
## Errors in entity functions
442541
Exception handling in entity functions depends on the Durable Functions hosting model:
443542

0 commit comments

Comments
 (0)