Problem
The newFailureDetails() function in packages/durabletask-js/src/utils/pb-helper.util.ts (line 188) converts JavaScript errors into protobuf TaskFailureDetails messages but never populates the innerFailure field, even when the source error has a .cause property.
The protobuf TaskFailureDetails message has a recursive innerFailure field specifically designed to preserve error chains:
message TaskFailureDetails {
string errorType = 1;
string errorMessage = 2;
google.protobuf.StringValue stackTrace = 3;
TaskFailureDetails innerFailure = 4;
bool isNonRetriable = 5;
}
However, setInnerfailure() is never called anywhere in the codebase.
Root Cause
The newFailureDetails() function extracts name, message, and stack from the error but does not check for error.cause (the ES2022 standard for error chaining).
Impact
The codebase creates errors with { cause: ... } in 9+ places (client.ts, entity-executor.ts, pb-helper.util.ts). When these errors are reported as orchestration/activity failures, the inner cause information is completely lost. This makes debugging harder because:
parseJsonField wraps SyntaxError in a descriptive error with { cause: err } — the original parse error is lost
- Client operations (rewind, restart) wrap gRPC errors with
{ cause: e } — the original gRPC error details are lost
- Entity state serialization errors wrap the original error — the cause is lost
Cross-SDK impact: The .NET SDK populates InnerFailure from InnerException. The JS SDK should match this behavior.
Proposed Fix
Recursively populate innerFailure from error.cause in newFailureDetails(), with a depth limit (10) to guard against pathological circular cause chains.
Problem
The
newFailureDetails()function inpackages/durabletask-js/src/utils/pb-helper.util.ts(line 188) converts JavaScript errors into protobufTaskFailureDetailsmessages but never populates theinnerFailurefield, even when the source error has a.causeproperty.The protobuf
TaskFailureDetailsmessage has a recursiveinnerFailurefield specifically designed to preserve error chains:However,
setInnerfailure()is never called anywhere in the codebase.Root Cause
The
newFailureDetails()function extractsname,message, andstackfrom the error but does not check forerror.cause(the ES2022 standard for error chaining).Impact
The codebase creates errors with
{ cause: ... }in 9+ places (client.ts, entity-executor.ts, pb-helper.util.ts). When these errors are reported as orchestration/activity failures, the inner cause information is completely lost. This makes debugging harder because:parseJsonFieldwrapsSyntaxErrorin a descriptive error with{ cause: err }— the original parse error is lost{ cause: e }— the original gRPC error details are lostCross-SDK impact: The .NET SDK populates
InnerFailurefromInnerException. The JS SDK should match this behavior.Proposed Fix
Recursively populate
innerFailurefromerror.causeinnewFailureDetails(), with a depth limit (10) to guard against pathological circular cause chains.