Repository navigation
fix: preserve received failure properties - #375
Conversation
Retain structured backend properties in public failure details, retry inspection, entity and testing projections, and forwarded task failure chains. Do not collect arbitrary JavaScript Error fields. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e0af01a5-0dfa-4e71-a660-c4186e65d7e0
Real Azure DTS validation — PASS at
|
| Live scenario | Expected and observed result |
|---|---|
| Uncaught activity failure | One activity attempt; terminal Failed state retains the task wrapper and rich root/nested received properties. |
| Catch and rethrow | One activity attempt; catch inspection/custom status sees the exact values, and terminal Failed state retains them after forwarding. |
| Custom retry handler | Handler reads received properties; the second activity attempt succeeds and the orchestration completes. A property named retryable:false does not automatically override SDK retry policy. |
| Policy callback inside a child orchestration | Callback reads properties and refuses retry; one activity attempt; child failure reaches the parent with the expected two task-wrapper levels and intact properties. |
Client get, waits, payload-enabled query (fetchInputsAndOutputs:true) and history match the expected values. Root and inner maps preserve strings, fractional numbers, 0, false, "", null, arrays and nested objects. Identically named keys with different root/inner values remain distinct; dt:/dto: strings remain literal strings.
What is real, and what is the test fixture?
A real JS activity executes and throws. Because this PR intentionally does not collect arbitrary JavaScript Error fields, a test-only producer hook adds valid protobuf property maps to the failed activity completion before that request is sent to Azure. Azure then persists and redispatches the failure to the unchanged JS worker. No inbound work item, history event, backend response or client result is replaced or modified.
Five observations came from actual service work items; four additional records came from persisted client history. Each persisted TaskFailed protobuf digest equals the corresponding outbound producer failure digest (159c039158c3575d531748ed87195dd564d8520180b93aba8730e0b94a1d0f7c). This is a real Azure transport/persistence/dispatch test with an explicitly constructed producer payload, not an actual .NET-worker interoperability run, and not a mocked backend.
Fixture correction and cleanup
The first run stopped at its query assertion because the harness omitted fetchInputsAndOutputs, whose default is false. The service returned status without failure details. Setting that query option to true was the only fixture correction; exact property assertions and production source were unchanged. The initial run/report/harness remain archived rather than counted as a pass.
All five instances from the successful run plus the first-run root were individually purged (deletedInstanceCount=1 each) and read back absent. All workers, clients and the test process stopped; the overlay was archived and removed, leaving a clean checkout at the tested head. All task-owned Azure resources are now deleted: temporary data role, task hub, scheduler and resource group. Azure confirmed the group absent at 2026-09-28T20:40:19Z. No customer resources were touched.
Evidence index: pr375-azure-validation-summary.json, full report pr375-5d8f4b8f-b3cb-4a8d-93ba-fa4bea123179.json, execution log and exact archived overlay. Overlay SHA-256: 3b03938093f886d71d89de39eda6f57117e114296f4b8a4551d5390836b45576.
Entities, the Functions testing adapter, cyclic user mutations and reserved-key handling retain local-only coverage; no cloud claims are made for those paths. No production source/dependency changes, commits, pushes, labels or merge actions were required.
Summary
What changed?
TaskFailureDetails.propertiesalready present on the wire. After: optional readonly properties survive task errors, retry callbacks, client state/waits/query/history, entity failure conversion, and in-memory/Functions testing.TaskFailedErrorwrapper when uncaught or rethrown. Preserve constructor compatibility, messages, explicit cause precedence, and existing failure-chain cycle markers.Why is this change needed?
Issues / work items
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):
Human verification boxes are intentionally left for the reviewer; agent-run evidence follows.
Testing
Automated tests
npm run build:coreandnpm run build -w durable-functionspassed; the latter also rebuilds core.git diff --checkpassed. Prettier passed for the eight changed files with clean formatting baselines (line endings normalized for comparison); existing formatting debt in six other files was left untouched rather than causing unrelated churn.Manual validation (only if runtime/behavior changed)
895fe72: four live scenarios passed on real production Azure DTS, covering uncaught/rethrown failures, custom-handler retry and policy/child propagation. A producer-only fixture adds valid properties before the activity completion is submitted; Azure persistence, dispatch and client reads are real and unmodified. Query usesfetchInputsAndOutputs:true. Exact-head evidence, initial fixture correction, cleanup and coverage limits. No actual .NET worker or live Functions host was used; entity/Functions adapter/cyclic-input paths remain local-only coverage.Notes for reviewers
undefined(not .NET's empty dictionary). Wire strings, includingdt:/dto:prefixes, remain strings; no CLR date reconstruction.__proto__map entries before SDK conversion; this dependency limitation is documented, not patched globally or in generated code. Adding that key before forwarding is rejected rather than silently lost.