Summary
The documentation does not explicitly describe what happens when the Lambda
runtime is terminated while a step is still executing — for example, when a
step's work takes longer than the function's invocation timeout (up to the
15-minute Lambda maximum), the sandbox crashes, or the SDK is unable to reach
the checkpoint API.
In this situation:
- The step never completes, so no
StepFailed event is recorded in the
execution history — only an InvocationCompleted event with an error
(e.g. Sandbox.Timedout).
- Because the step is not marked as failed, the step's retry strategy /
retry budget is not consumed.
- Lambda retries the invocation, replays the execution, and (with default
at-least-once step semantics) re-runs the same incomplete step from the
beginning.
- If the step deterministically takes longer than the invocation timeout,
this repeats indefinitely until the durable execution's ExecutionTimeout
is reached or the execution is stopped manually.
This is surprising if you expect a step retry strategy (e.g. "max 3
attempts") to bound how many times a step's code can run. That bound only
applies to failures the SDK observes and checkpoints — not to invocation-level
interruptions.
What the docs cover today
- The step operation and error-handling pages describe
StepInterruptedError for at-most-once steps that Lambda interrupted
before the result was checkpointed.
- The idempotency best-practices page explains at-most-once vs
at-least-once semantics and mentions that a process crash or invocation
timeout can cause a step to run again.
What's missing is an explicit, discoverable treatment of the
runaway-step failure mode above and the recommended mitigations.
Proposed documentation changes
-
Describe the failure mode explicitly, ideally in
patterns/best-practices/step-design.md (or a dedicated section in the
error-handling docs): when the runtime is terminated mid-step (invocation
timeout, sandbox crash, inability to checkpoint), no StepFailed event
is recorded, retry budgets are not consumed, and the step restarts on the
next invocation. Include the concrete consequence: a step whose work
always exceeds the invocation timeout will loop until ExecutionTimeout.
-
Document the mitigations:
- Keep step work well under the invocation timeout. A single step's
execution must fit inside one invocation; split long-running work into
smaller steps, or move it out of the function entirely (e.g.
invoke, callbacks).
- Use at-most-once-per-retry semantics for steps that might be
interrupted, so the interruption is detected (StepInterruptedError /
StepInterruptedException) and subject to the retry strategy instead
of silently restarting.
- Implement an in-step timeout so the step fails (and checkpoints the
failure) before the sandbox is terminated:
- TypeScript: race the work against a timeout promise and use an
AbortController to cancel it.
- Java:
DurableFuture.anyOf(longStep, context.wait(...)) — see the
WaitExample in the Java SDK.
- Python / C#: equivalent cancellation patterns.
-
Cross-link the new content from:
sdk-reference/operations/step.md (retry strategy section — clarify
what the retry budget does and does not bound)
sdk-reference/error-handling/retries.md
patterns/best-practices/idempotency.md
getting-started/key-concepts.md (Timeouts section — note that the
function timeout also bounds how long any single step may run)
Why this matters
Without this guidance, a single misbehaving step can consume the entire
execution timeout in silent retry loops, with nothing in the step-level
history indicating a failure. Users currently have to discover the pattern
themselves (e.g. by reading execution history at startup and detecting
consecutive InvocationCompleted errors with no durable progress).
Summary
The documentation does not explicitly describe what happens when the Lambda
runtime is terminated while a step is still executing — for example, when a
step's work takes longer than the function's invocation timeout (up to the
15-minute Lambda maximum), the sandbox crashes, or the SDK is unable to reach
the checkpoint API.
In this situation:
StepFailedevent is recorded in theexecution history — only an
InvocationCompletedevent with an error(e.g.
Sandbox.Timedout).retry budget is not consumed.
at-least-once step semantics) re-runs the same incomplete step from the
beginning.
this repeats indefinitely until the durable execution's
ExecutionTimeoutis reached or the execution is stopped manually.
This is surprising if you expect a step retry strategy (e.g. "max 3
attempts") to bound how many times a step's code can run. That bound only
applies to failures the SDK observes and checkpoints — not to invocation-level
interruptions.
What the docs cover today
StepInterruptedErrorfor at-most-once steps that Lambda interruptedbefore the result was checkpointed.
at-least-once semantics and mentions that a process crash or invocation
timeout can cause a step to run again.
What's missing is an explicit, discoverable treatment of the
runaway-step failure mode above and the recommended mitigations.
Proposed documentation changes
Describe the failure mode explicitly, ideally in
patterns/best-practices/step-design.md(or a dedicated section in theerror-handling docs): when the runtime is terminated mid-step (invocation
timeout, sandbox crash, inability to checkpoint), no
StepFailedeventis recorded, retry budgets are not consumed, and the step restarts on the
next invocation. Include the concrete consequence: a step whose work
always exceeds the invocation timeout will loop until
ExecutionTimeout.Document the mitigations:
execution must fit inside one invocation; split long-running work into
smaller steps, or move it out of the function entirely (e.g.
invoke, callbacks).interrupted, so the interruption is detected (
StepInterruptedError/StepInterruptedException) and subject to the retry strategy insteadof silently restarting.
failure) before the sandbox is terminated:
AbortControllerto cancel it.DurableFuture.anyOf(longStep, context.wait(...))— see theWaitExample in the Java SDK.
Cross-link the new content from:
sdk-reference/operations/step.md(retry strategy section — clarifywhat the retry budget does and does not bound)
sdk-reference/error-handling/retries.mdpatterns/best-practices/idempotency.mdgetting-started/key-concepts.md(Timeouts section — note that thefunction timeout also bounds how long any single step may run)
Why this matters
Without this guidance, a single misbehaving step can consume the entire
execution timeout in silent retry loops, with nothing in the step-level
history indicating a failure. Users currently have to discover the pattern
themselves (e.g. by reading execution history at startup and detecting
consecutive
InvocationCompletederrors with no durable progress).