Skip to content

docs(typescript): align "Step interrupted" guidance with SDK behaviour #192

@yaythomas

Description

@yaythomas

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

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions