Summary
The TypeScript tab of the Error handling: errors page tells customers to detect step interruption with instanceof StepInterruptedError, but the JS SDK never throws StepInterruptedError from await context.step(...). The StepError-with-cause wrapping has been the SDK's behaviour since this code path was introduced.
A separate bug (aws-durable-execution-sdk-js#529) masked the divergence: markOperationState ran without metadata on the shouldRetry: false branch, so on a cold replay the SDK threw "metadata required on first call" before reaching the throw line. Customers in this scenario saw an internal error, not the wrapped StepError. aws-durable-execution-sdk-js#576 fixes that crash and codifies the wrapping contract via unit and e2e tests, so the docs/SDK divergence becomes visible to customers who previously hit the crash.
Python and Java tabs remain accurate. Those SDKs raise or throw the language-specific StepInterruptedError or StepInterruptedException directly per their respective hierarchies. Only the TypeScript tab needs changes.
What the JS SDK actually throws
The interrupted-step + AT_MOST_ONCE_PER_RETRY path in step-handler.ts routes the error through DurableOperationError.fromErrorObject(createErrorObjectFromError(error)). StepInterruptedError extends Error, not DurableOperationError, so fromErrorObject falls through its switch to the default branch and returns a fresh StepError. The new StepError carries a plain Error on its cause field with cause.name === "StepInterruptedError". So:
err instanceof StepInterruptedError evaluates to false
err instanceof StepError evaluates to true
err instanceof DurableOperationError evaluates to true
err.cause instanceof StepInterruptedError evaluates to false (the cause is a plain Error, not the original sentinel)
err.cause?.name === "StepInterruptedError" evaluates to true (the only working detector)
Per the discussion on aws-durable-execution-sdk-js#569, StepInterruptedError is an internal sentinel that the SDK passes only to the user's retryStrategy(error, attempt) callback. Everything thrown out of await context.step(...) is a DurableOperationError subclass.
Files to change
-
docs/sdk-reference/error-handling/errors.md, TypeScript tab of "Exception types": replace the paragraph about StepInterruptedError to describe its role as a retryStrategy callback sentinel.
-
docs/sdk-reference/error-handling/errors.md, TypeScript mermaid diagram: relabel the SIE node so the diagram does not imply StepInterruptedError surfaces as a thrown error.
-
docs/sdk-reference/operations/step.md, TypeScript tab in two places (the step() Throws note and the AtMostOncePerRetry semantics description): describe the StepError-with-cause shape.
-
examples/typescript/sdk-reference/error-handling/step-interrupted.ts: update the catch block to detect via the cause:
if (err instanceof StepError && err.cause?.name === "StepInterruptedError") {
...
}
and update the imports.
-
examples/typescript/sdk-reference/error-handling/exception-hierarchy.ts: update the trailing comment block. Keep StepInterruptedError in the import/export list because it remains useful for typing the error argument of a retryStrategy callback.
Out of scope
- Python and Java tabs of the affected pages.
examples/typescript/sdk-reference/error-handling/step-interrupted-error.ts (currently a // Coming soon... placeholder).
Context
A docs PR will follow.
Summary
The TypeScript tab of the Error handling: errors page tells customers to detect step interruption with
instanceof StepInterruptedError, but the JS SDK never throwsStepInterruptedErrorfromawait context.step(...). TheStepError-with-causewrapping has been the SDK's behaviour since this code path was introduced.A separate bug (aws-durable-execution-sdk-js#529) masked the divergence:
markOperationStateran without metadata on theshouldRetry: falsebranch, so on a cold replay the SDK threw"metadata required on first call"before reaching the throw line. Customers in this scenario saw an internal error, not the wrappedStepError. aws-durable-execution-sdk-js#576 fixes that crash and codifies the wrapping contract via unit and e2e tests, so the docs/SDK divergence becomes visible to customers who previously hit the crash.Python and Java tabs remain accurate. Those SDKs raise or throw the language-specific
StepInterruptedErrororStepInterruptedExceptiondirectly per their respective hierarchies. Only the TypeScript tab needs changes.What the JS SDK actually throws
The interrupted-step +
AT_MOST_ONCE_PER_RETRYpath instep-handler.tsroutes the error throughDurableOperationError.fromErrorObject(createErrorObjectFromError(error)).StepInterruptedErrorextendsError, notDurableOperationError, sofromErrorObjectfalls through itsswitchto thedefaultbranch and returns a freshStepError. The newStepErrorcarries a plainErroron itscausefield withcause.name === "StepInterruptedError". So:err instanceof StepInterruptedErrorevaluates tofalseerr instanceof StepErrorevaluates totrueerr instanceof DurableOperationErrorevaluates totrueerr.cause instanceof StepInterruptedErrorevaluates tofalse(the cause is a plainError, not the original sentinel)err.cause?.name === "StepInterruptedError"evaluates totrue(the only working detector)Per the discussion on aws-durable-execution-sdk-js#569,
StepInterruptedErroris an internal sentinel that the SDK passes only to the user'sretryStrategy(error, attempt)callback. Everything thrown out ofawait context.step(...)is aDurableOperationErrorsubclass.Files to change
docs/sdk-reference/error-handling/errors.md, TypeScript tab of "Exception types": replace the paragraph aboutStepInterruptedErrorto describe its role as aretryStrategycallback sentinel.docs/sdk-reference/error-handling/errors.md, TypeScript mermaid diagram: relabel theSIEnode so the diagram does not implyStepInterruptedErrorsurfaces as a thrown error.docs/sdk-reference/operations/step.md, TypeScript tab in two places (thestep()Throws note and theAtMostOncePerRetrysemantics description): describe theStepError-with-causeshape.examples/typescript/sdk-reference/error-handling/step-interrupted.ts: update the catch block to detect via the cause:and update the imports.
examples/typescript/sdk-reference/error-handling/exception-hierarchy.ts: update the trailing comment block. KeepStepInterruptedErrorin the import/export list because it remains useful for typing theerrorargument of aretryStrategycallback.Out of scope
examples/typescript/sdk-reference/error-handling/step-interrupted-error.ts(currently a// Coming soon...placeholder).Context
A docs PR will follow.