From cb942e498052e8ed06f7ef71550fcf7e0b6dd423 Mon Sep 17 00:00:00 2001 From: "naiyuantian@microsoft.com" Date: Thu, 7 May 2026 09:23:46 -0700 Subject: [PATCH] initial commit --- .../OutOfProcMiddleware.cs | 3 +- ...ustomExceptionPropertiesOrchestration.java | 62 ++++++++++++++++ .../BusinessValidationException.java | 73 +++++++++++++++++++ test/e2e/Tests/Tests/ErrorHandlingTests.cs | 2 +- 4 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 test/e2e/Apps/BasicJava/src/main/java/com/function/CustomExceptionPropertiesOrchestration.java create mode 100644 test/e2e/Apps/BasicJava/src/main/java/com/function/exceptions/BusinessValidationException.java diff --git a/src/WebJobs.Extensions.DurableTask/OutOfProcMiddleware.cs b/src/WebJobs.Extensions.DurableTask/OutOfProcMiddleware.cs index 279368df2..24259ca79 100644 --- a/src/WebJobs.Extensions.DurableTask/OutOfProcMiddleware.cs +++ b/src/WebJobs.Extensions.DurableTask/OutOfProcMiddleware.cs @@ -683,7 +683,8 @@ private static FailureDetails GetFailureDetails(Exception e, out bool fromSerial return details; } - // For non-.NET language, properties at FailureDetails is not supported yet. + // For non-.NET languages that don't serialize exception as JSON TaskFailureDetails, + // properties are not available in the fallback path. if (TrySplitExceptionTypeFromMessage(exception, out string? exceptionType, out string? exceptionMessage)) { return new FailureDetails(exceptionType, exceptionMessage, stackTrace, innerFailure: null, isNonRetriable: false, properties: null); diff --git a/test/e2e/Apps/BasicJava/src/main/java/com/function/CustomExceptionPropertiesOrchestration.java b/test/e2e/Apps/BasicJava/src/main/java/com/function/CustomExceptionPropertiesOrchestration.java new file mode 100644 index 000000000..8d925b1b1 --- /dev/null +++ b/test/e2e/Apps/BasicJava/src/main/java/com/function/CustomExceptionPropertiesOrchestration.java @@ -0,0 +1,62 @@ +package com.function; + +import com.microsoft.azure.functions.annotation.*; +import com.function.exceptions.BusinessValidationException; +import com.microsoft.azure.functions.*; + +import java.time.Instant; +import java.util.*; + +import com.microsoft.durabletask.*; +import com.microsoft.durabletask.azurefunctions.DurableActivityTrigger; +import com.microsoft.durabletask.azurefunctions.DurableOrchestrationTrigger; + + +/** + * Azure Functions with Durable Task for custom exception properties in failure details. + */ +public class CustomExceptionPropertiesOrchestration { + + /** + * Orchestrator: OrchestrationWithCustomException + * Calls BusinessActivity and catches TaskFailedException, returning the FailureDetails. + */ + @FunctionName("OrchestrationWithCustomException") + public FailureDetails orchestrationWithCustomException( + @DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) { + try { + ctx.callActivity("BusinessActivity", null, Void.class).await(); + } catch (TaskFailedException ex) { + return ex.getErrorDetails(); + } + + // Should never reach here. + return null; + } + + /** + * Activity: BusinessActivity + * Throws a BusinessValidationException with custom properties. + */ + @FunctionName("BusinessActivity") + public void businessActivity( + @DurableActivityTrigger(name = "input") String input, + final ExecutionContext context) throws BusinessValidationException { + Map dictionaryProperty = new LinkedHashMap<>(); + dictionaryProperty.put("error_code", "VALIDATION_FAILED"); + dictionaryProperty.put("retry_count", 3); + dictionaryProperty.put("is_critical", true); + + List listProperty = Arrays.asList("error1", "error2", 500, null); + + throw new BusinessValidationException( + "Business logic validation failed", + "validation-error-123", + 100, + 999999999L, + Instant.parse("2025-10-15T14:30:00Z"), + dictionaryProperty, + listProperty, + null); + } +} diff --git a/test/e2e/Apps/BasicJava/src/main/java/com/function/exceptions/BusinessValidationException.java b/test/e2e/Apps/BasicJava/src/main/java/com/function/exceptions/BusinessValidationException.java new file mode 100644 index 000000000..21c4251af --- /dev/null +++ b/test/e2e/Apps/BasicJava/src/main/java/com/function/exceptions/BusinessValidationException.java @@ -0,0 +1,73 @@ +package com.function.exceptions; + +import java.time.Instant; +import java.util.List; +import java.util.Map; + +public class BusinessValidationException extends Exception { + private final String stringProperty; + private final Integer intProperty; + private final Long longProperty; + private final Instant dateTimeProperty; + private final Map dictionaryProperty; + private final List listProperty; + private final Object nullProperty; + + public BusinessValidationException( + String message, + String stringProperty, + Integer intProperty, + Long longProperty, + Instant dateTimeProperty, + Map dictionaryProperty, + List listProperty, + Object nullProperty) { + super(message); + this.stringProperty = stringProperty; + this.intProperty = intProperty; + this.longProperty = longProperty; + this.dateTimeProperty = dateTimeProperty; + this.dictionaryProperty = dictionaryProperty; + this.listProperty = listProperty; + this.nullProperty = nullProperty; + } + + public BusinessValidationException(String message) { + super(message); + this.stringProperty = null; + this.intProperty = null; + this.longProperty = null; + this.dateTimeProperty = null; + this.dictionaryProperty = null; + this.listProperty = null; + this.nullProperty = null; + } + + public String getStringProperty() { + return stringProperty; + } + + public Integer getIntProperty() { + return intProperty; + } + + public Long getLongProperty() { + return longProperty; + } + + public Instant getDateTimeProperty() { + return dateTimeProperty; + } + + public Map getDictionaryProperty() { + return dictionaryProperty; + } + + public List getListProperty() { + return listProperty; + } + + public Object getNullProperty() { + return nullProperty; + } +} diff --git a/test/e2e/Tests/Tests/ErrorHandlingTests.cs b/test/e2e/Tests/Tests/ErrorHandlingTests.cs index 1ad23a8b1..7780d9378 100644 --- a/test/e2e/Tests/Tests/ErrorHandlingTests.cs +++ b/test/e2e/Tests/Tests/ErrorHandlingTests.cs @@ -225,7 +225,7 @@ await this.fixture.TestLogs.AssertLogExistsAsync( [Trait("PowerShell", "Skip")] // FailureDetails is a dotnet-isolated implementation detail [Trait("Python", "Skip")] // FailureDetails is a dotnet-isolated implementation detail [Trait("Node", "Skip")] // FailureDetails is a dotnet-isolated implementation detail - [Trait("Java", "Skip")] // Include exception properties at Failure Details for Java is not supported yet. + [Trait("Java", "Skip")] // Java SDK v1.6.0 doesn't serialize custom exception properties [Trait("DTS", "Skip")] // DTS doesn't support this feature yet. public async Task CustomExceptionPropertiesInFailureDetails() {