elsa-workflows / elsa-workflows/elsa-core
When an activity has a fault depending on how it's created there a different events raised
- Dominant language
- C#
- Stars
- 7.9k
- Forks
- 1.5k
- Avg merge
- 15h 22m
- Merged PRs (30d)
- 114
Description
## Description
If you subscribe to the `IEventHandler` and `IEventHandler` to track everything before and after an activity runs you can get different behaviour depending on how the fault is created.
For example setting the fault like
```cs
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
context.Fault(new Exception())
}
```
In this case you would get an event for `ActivityExecuting` and `ActivityExecuted`. However if you do this
```cs
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
throw new Exception();
}
````
You will only get `ActivityExecuting` because the Exception bubbles up the pipeline and skips `NotificationPublishingMiddleware` and gets processed by the `ExceptionHandlingMiddleware`.
## Steps to Reproduce
Create a notification class and subscribe to both events, have one activity throw an another set the fault. Notice the difference.
```cs
internal class SuperHandler :
INotificationHandler,
INotificationHandler
{
public Task HandleAsync(ActivityExecuted notification, CancellationToken cancellationToken)
{
// This gets called only if the activity does not use a `throw` statement to throw an exception.
return Task.CompletedTask;
}
public Task HandleAsync(ActivityExecuting notification, CancellationToken cancellationToken)
{
// This gets called for every activity
return Task.CompletedTask;
}
}
```
**Reproduction Rate**: 100% of the time
## Expected Behavior
This could be fixed in a few different ways
1. Move the exception handler up in the pipeline, I don't think this is a good idea as it won't catch exception from other middleware.
2. Handle exceptions from activities at the invocation level and use the exception handler for just exceptions at the middleware level.
3. Add a new event ActivityFaulted that looks like
```
public record ActivityFaulted(ActivityExecutionContext ActivityExecutionContext, Exception Falut) : INotification;
```
you would need to pass the exception because depending on how the fault is created the context would not be in the faulted state yet.
## Actual Behavior
You can't know (unless you write your own middleware) when events complete but become faulted due to an unhandled exception
Contributor guide
Assessment
This issue has not been assessed yet.