Repository navigation
Retry surfaces the typed error, not a SuppressedError wrapper #72
Description
Part of the audit remediation umbrella #67. Milestone 3. Severity: HIGH. Requirements: XCUT-1, RETRY error contract.
Current SDK behavior
When retry gives up and the attempt trail is not empty, withTrail
(packages/core/src/retry/engine.ts:260-271) wraps the final error:
suppress(outcome.error, folded, 'retry attempts exhausted'). The surfaced object is a
SuppressedError. The typed error is at .error. The surfaced type therefore depends on how
many attempts ran:
| Scenario | Surfaced error |
|---|---|
maxAttempts: 1, transport failure |
TransportFailureError |
maxAttempts: 3 (default), same failure |
SuppressedError |
abort during backoff, maxAttempts: 5 |
SuppressedError; instanceof CancellationError is false |
The third row is the case the comment at :378-383 says abortToSdkError exists to fix. The
mapping is applied at :385 and undone by withTrail at :386. A backoff cancellation always
has a non-empty trail. XCUT-1's conformance clause is "assert the surfaced error is the
cancellation type". docs/sdk-documentation/write-a-response-handler.md:75-76 documents the
wrapper only for the RECOV-12 release-failure path.
Expected behavior
The error that retryStep and dispatchWithRetry surface is the typed error of the final
attempt. instanceof on that error gives the same result for one attempt and for many. A
cancellation during backoff surfaces CancellationError. The earlier attempts' errors remain
reachable from the surfaced error.
Notes and leads
- Keep the final error primary. Attach the trail without changing the object's class: a
causechain, anerrorsarray property (theAggregateErrorshape), or a documented
attemptsproperty on the typed leaf. If a dedicatedRetryExhaustedErroris wanted, it must
still not hideCancellationError. suppress()(packages/core/src/suppress.ts) is the right tool for release failures, not for
attempt history.- Update
write-a-response-handler.mdand theretryStepTSDoc. - Tests:
instanceof TransportFailureErrorformaxAttempts1 and 3;instanceof CancellationErroron abort during backoff; the trail is reachable. Add a row to
tests/conformance/xcut/retry-safety.conformance.test.tsand
cancellation-and-timeout.conformance.test.ts. - Behavior change: minor changeset for
@dexpace/core. Record the trail shape in the Phase 5a
ledger section.
Activity
docs(retry): the trail is not an attempt count — drop the false arith…
fix(retry): surface the final typed error, not a SuppressedError wrapper
test(xcut): conformance rows for the surfaced retry error and its trail
docs: the retry pillar throws the last attempt's error, and retryAtte…
test(node): the retry trail case moves from "same wrapper" to "no wra…
docs(retry): the trail is not an attempt count — drop the false arith…
docs: ledger — wave 3 landed; re-anchor deviations item 3 after #72
Wahbeh-Mohammad commentedon Sep 5, 2026
Addressed in PR #88, merged into
audit/remediation-67; reachesmvpthrough PR #98. Decision record:docs/audit-67-decisions.md.