Repository navigation
fix: preserve nested task failure details - #371
Merged
Merged
Conversation
Expose innerFailure throughout task errors, retry inspection, client state, history and in-memory testing. Keep received details below TaskFailedError when an uncaught or rethrown failure is serialized. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e0af01a5-0dfa-4e71-a660-c4186e65d7e0
Contributor
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
The Functions testing adapter must recursively project inner failures into documented plain-object fields.
Review effort: Lite
Findings: None
What changed in this PR
This PR preserves nested task failure details across public APIs, retries, history, clients, gRPC, and testing adapters.
Changes:
- Adds recursive
innerFailuresupport and protobuf conversion. - Preserves failure chains through serialization and propagation.
- Adds focused tests, documentation, and changelog updates.
| File | Summary |
|---|---|
README.md |
Documents nested failure inspection. |
packages/durabletask-js/test/retry-handler-task.spec.ts |
Tests nested retry inspection. |
packages/durabletask-js/test/failure-details.spec.ts |
Tests failure-chain propagation. |
packages/durabletask-js/test/failure-details-grpc.spec.ts |
Tests gRPC propagation. |
packages/durabletask-js/src/utils/pb-helper.util.ts |
Preserves received failure chains. |
packages/durabletask-js/src/utils/history-event-converter.ts |
Converts nested history failures. |
packages/durabletask-js/src/utils/failure-details.util.ts |
Centralizes recursive conversion. |
packages/durabletask-js/src/testing/test-client.ts |
Preserves in-memory client chains. |
packages/durabletask-js/src/testing/in-memory-backend.ts |
Forwards sub-orchestration details. |
packages/durabletask-js/src/task/retryable-task.ts |
Exposes nested details to retry policies. |
packages/durabletask-js/src/task/retry/retry-context.ts |
Documents nested retry failures. |
packages/durabletask-js/src/task/retry-handler-task.ts |
Exposes nested details to retry handlers. |
packages/durabletask-js/src/task/failure-details.ts |
Adds recursive public failure details. |
packages/durabletask-js/src/task/exception/task-failed-error.ts |
Preserves converted nested details. |
packages/durabletask-js/src/orchestration/index.ts |
Preserves orchestration failure chains. |
packages/durabletask-js/src/client/client.ts |
Preserves client failure chains. |
packages/azure-functions-durable/test/unit/testing.spec.ts |
Tests nested Functions testing failures. |
packages/azure-functions-durable/src/testing/index.ts |
Forwards nested testing failures. |
packages/azure-functions-durable/CHANGELOG.md |
Documents the adapter change. |
CHANGELOG.md |
Documents the core change. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Track received failure objects by identity and append a CircularFailureDetails marker on a repeated reference. Preserve the task wrapper and unique prefix without truncating finite chains or changing ordinary Error.cause handling. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e0af01a5-0dfa-4e71-a660-c4186e65d7e0
kaibocai (kaibocai)
approved these changes
Sep 24, 2026
4 of 15 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
What changed?
Preserve the received
innerFailurechain in public task failure details, activity/sub-orchestration errors, retry handlers and predicates, client get/wait/query state, and history.Add a read-only optional
TaskFailureDetails.innerFailure, a matchingFailureDetailsgetter and optional fourth constructor argument. Existing constructor arguments and the taskmessage/ entityerrorMessageAPIs are unchanged.Use one internal protobuf-to-public converter. Retain original sub-orchestration failure protobufs in the in-memory backend and forward the chain through
durable-functions/testing.Preserve received details beneath the existing
TaskFailedErrorwrapper when an uncaught or rethrown task failure is serialized. This narrow writer change is necessary for activity failures to reach terminal client state intact; it does not reconstruct JavaScriptError.causeobjects.Follow-up
6f0aeae: guard received-detail forwarding with an object-identitySet. A user-mutated cycle preserves the task wrapper and each unique failure, then ends with an explicitCircularFailureDetailsmarker; finite chains are not depth-truncated.Why is this change needed?
Error.cause, but public readers discarded all inner failures. AnOrderFailed -> PaymentFailed -> ConnectionTimeoutchain therefore disappeared before orchestrator catch blocks, retry handlers, and clients could inspect it.Issues / work items
main, not stacked on another open PR.Project checklist
CHANGELOG.mdAI-assisted code disclosure (required)
Was an AI tool used? (select one)
If AI was used:
AI verification (required if AI was used):
Testing
Automated tests
6f0aeae): 5 suites / 78 tests passed, core build, scoped ESLint, normal hooks and patch checks passed. Self-cycle, three-node cycle and caught/mutated/rethrown failure regressions first failed safely before the fix; a bounded test getter prevents unbounded allocation during RED. Distinct equal-valued objects, existing >10-level finite chains and explicit JavaScript cause precedence remain covered.fd684cf) core focused run: 17 suites, 382 tests. Covers failure details, both retry mechanisms/task kinds, executor, state, client waits/query, history, in-memory serialization, entity compatibility and the existing writer depth boundary.npm run build:coreand the affected Functions TypeScript build passed.git diff --check.Manual validation (only if runtime/behavior changed)
Notes for reviewers
undefined; empty strings and optional stack traces are preserved. Reading or forwarding received wire details does not add a depth cap. Forwarding detects repeated object identities and appends{ errorType: "CircularFailureDetails", message: "A circular innerFailure reference was detected." }as a terminal marker, rather than hanging or silently dropping the cause. This guards mutable JavaScript details; it is not a new retry rule.Error.causewriter still has its existing ten-inner-level bound. Explicitly assigned JavaScript causes retain precedence.errorMessageshape. No protocol/generated-file changes, new dependencies, configuration, resources, or unrelated parity features are included.