Skip to content

Commit 21153b4

Browse files
authored
Improve Error Type Accuracy in TaskFailedException FailureDetails with .NET-Isolated app (#3070)
* initial commit * fix index * Fix typos in function name. * Make TaskFailureDetails nullable in method signature * update durabletask.abstraction ver
1 parent b9dd313 commit 21153b4

File tree

5 files changed

+66
-4
lines changed

5 files changed

+66
-4
lines changed

Directory.Packages.props

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
<PackageVersion Include="Microsoft.CodeAnalysis" Version="3.9.0" />
6565
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="3.9.0" />
6666
<PackageVersion Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.65" />
67+
<PackageVersion Include="Microsoft.DurableTask.Abstractions" Version="1.8.1" />
6768
<PackageVersion Include="Microsoft.DurableTask.Generators" Version="1.0.0-preview.1" />
6869
<PackageVersion Include="Microsoft.DurableTask.SqlServer.AzureFunctions" Version="1.5.0" />
6970
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="9.0.0" />

src/Worker.Extensions.DurableTask/Exceptions/DurableSerializationException.cs

+8-2
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,20 @@ internal class DurableSerializationException : Exception
1111
// We set the base class properties of this exception to the same as the parent,
1212
// so that methods in the worker after this can still (typically) access the same information vs w/o
1313
// this exception type.
14-
internal DurableSerializationException(Exception fromException) : base(fromException.Message, fromException.InnerException)
14+
internal DurableSerializationException(Exception fromException) : base(CreateExceptionMessage(fromException), fromException.InnerException)
1515
{
1616
this.fromException = fromException;
1717
}
1818

1919
public override string ToString()
2020
{
21-
TaskFailureDetails? failureDetails = TaskFailureDetailsConverter.TaskFailureFromException(this.fromException);
21+
return this.Message;
22+
}
23+
24+
// Serilize FailureDetails to JSON
25+
private static string CreateExceptionMessage(Exception ex)
26+
{
27+
TaskFailureDetails? failureDetails = TaskFailureDetailsConverter.TaskFailureFromException(ex);
2228
return JsonConvert.SerializeObject(failureDetails);
2329
}
2430

test/e2e/Apps/BasicDotNetIsolated/ActivityErrorHandling.cs

+32-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,22 @@ public static async Task<HttpResponseData> CatchHttpStart(
4646
return await client.CreateCheckStatusResponseAsync(req, instanceId);
4747
}
4848

49+
[Function("CatchActivityExceptionFailureDetails_HttpStart")]
50+
public static async Task<HttpResponseData> CatchFailureDetailsHttpStart(
51+
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
52+
[DurableClient] DurableTaskClient client,
53+
FunctionContext executionContext)
54+
{
55+
ILogger logger = executionContext.GetLogger("CatchActivityExceptionFailureDetails_HttpStart");
56+
57+
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
58+
nameof(CatchActivityExceptionFailureDetails));
59+
60+
logger.LogInformation("Started orchestration with ID = '{instanceId}'.", instanceId);
61+
62+
return await client.CreateCheckStatusResponseAsync(req, instanceId);
63+
}
64+
4965
[Function("RetryActivityException_HttpStart")]
5066
public static async Task<HttpResponseData> RetryHttpStart(
5167
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
@@ -96,11 +112,26 @@ public static async Task<string> CatchActivityException(
96112
return output;
97113
}
98114
catch (TaskFailedException ex)
99-
{
115+
{
100116
return ex.Message;
101117
}
102118
}
103119

120+
[Function(nameof(CatchActivityExceptionFailureDetails))]
121+
public static async Task<TaskFailureDetails?> CatchActivityExceptionFailureDetails(
122+
[OrchestrationTrigger] TaskOrchestrationContext context)
123+
{
124+
try
125+
{
126+
await context.CallActivityAsync<string>(nameof(RaiseException), context.InstanceId);
127+
return null;
128+
}
129+
catch (TaskFailedException ex)
130+
{
131+
return ex.FailureDetails;
132+
}
133+
}
134+
104135
[Function(nameof(RetryActivityFunction))]
105136
public static async Task<string> RetryActivityFunction(
106137
[OrchestrationTrigger] TaskOrchestrationContext context)

test/e2e/Tests/E2ETests.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13+
<PackageReference Include="Microsoft.DurableTask.Abstractions"/>
1314
<PackageReference Include="Microsoft.Extensions.Configuration" />
1415
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
1516
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />

test/e2e/Tests/Tests/ErrorHandlingTests.cs

+24-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

44
using System.Net;
5+
using Microsoft.DurableTask;
6+
using Newtonsoft.Json;
57
using Xunit;
68
using Xunit.Abstractions;
79

@@ -71,6 +73,27 @@ public async Task OrchestratorWithCaughtActivityException_ShouldSucceed()
7173
Assert.Contains("This activity failed", orchestrationDetails.Output);
7274
}
7375

76+
[Fact]
77+
public async Task OrchestratorWithCaughtActivityExceptionFailuredetails_ContainRightErrorType()
78+
{
79+
using HttpResponseMessage response = await HttpHelpers.InvokeHttpTrigger("CatchActivityExceptionFailureDetails_HttpStart", "");
80+
81+
Assert.Equal(HttpStatusCode.Accepted, response.StatusCode);
82+
string statusQueryGetUri = await DurableHelpers.ParseStatusQueryGetUriAsync(response);
83+
84+
await DurableHelpers.WaitForOrchestrationStateAsync(statusQueryGetUri, "Completed", 30);
85+
86+
var orchestrationDetails = await DurableHelpers.GetRunningOrchestrationDetailsAsync(statusQueryGetUri);
87+
// Deserialize the output to FailureDetails
88+
var failureDetails = JsonConvert.DeserializeObject<TaskFailureDetails>(orchestrationDetails.Output);
89+
90+
// Check FailureDetails contains the right error type and error message,
91+
// Here it should be the same one as the activity function Raise Exception throws.
92+
Assert.NotNull(failureDetails);
93+
Assert.Equal("System.InvalidOperationException", failureDetails.ErrorType);
94+
Assert.Equal("This activity failed", failureDetails.ErrorMessage);
95+
}
96+
7497
[Fact]
7598
[Trait("MSSQL", "Skip")] // Durable Entities are not supported in MSSQL/Dotnet Isolated, see https://github.com/microsoft/durabletask-mssql/issues/205
7699
public async Task OrchestratorWithCaughtEntityException_ShouldSucceed()

0 commit comments

Comments
 (0)