Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/WebJobs.Extensions.DurableTask/OutOfProcMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Comment on lines +33 to +34
}

/**
* Activity: BusinessActivity
* Throws a BusinessValidationException with custom properties.
*/
@FunctionName("BusinessActivity")
public void businessActivity(
@DurableActivityTrigger(name = "input") String input,
final ExecutionContext context) throws BusinessValidationException {
Map<String, Object> dictionaryProperty = new LinkedHashMap<>();
dictionaryProperty.put("error_code", "VALIDATION_FAILED");
dictionaryProperty.put("retry_count", 3);
dictionaryProperty.put("is_critical", true);

List<Object> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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<String, Object> dictionaryProperty;
private final List<Object> listProperty;
private final Object nullProperty;

public BusinessValidationException(
String message,
String stringProperty,
Integer intProperty,
Long longProperty,
Instant dateTimeProperty,
Map<String, Object> dictionaryProperty,
List<Object> 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<String, Object> getDictionaryProperty() {
return dictionaryProperty;
}

public List<Object> getListProperty() {
return listProperty;
}

public Object getNullProperty() {
return nullProperty;
}
}
2 changes: 1 addition & 1 deletion test/e2e/Tests/Tests/ErrorHandlingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Comment on lines +228 to 229
public async Task CustomExceptionPropertiesInFailureDetails()
{
Expand Down
Loading