aws / aws/aws-durable-execution-sdk-js
[Feature]: Exit gracefully with PENDING when a checkpoint response has no CheckpointToken
- Dominant language
- TypeScript
- Stars
- 84
- Forks
- 28
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 43
Description
### What would you like?
When a checkpoint response arrives without a `CheckpointToken`, the SDK should treat it as a signal that the service will accept no further checkpoints from this invocation. The SDK should stop issuing checkpoints and end the invocation cleanly with `Status: PENDING`.
Why PENDING and not FAILED or a thrown error:
- A missing token does not mean the execution is finished. It means this invocation cannot make further progress. The invocation result must not claim the execution finished.
- A thrown error is an invocation failure. Lambda retries it, but the retry has no valid token and can do nothing useful. It also puts an error in customer logs for a condition the SDK understood.
- PENDING is already what the SDK returns for every suspend (wait, scheduled retry, pending callback). This is a suspend: the invocation is done for now and the execution continues later.
### Current behavior
The checkpoint manager ignores a missing token (`src/utils/checkpoint/checkpoint-manager.ts`):
```ts
if (response.CheckpointToken) {
this.currentTaskToken = response.CheckpointToken;
}
```
So the SDK keeps the consumed token and sends it on the next checkpoint. The service rejects that call with `InvalidParameterValueException: Invalid checkpoint token`. With the fix for #917 in place, that is classified as an invocation error and the handler throws. So the invocation ends with a Lambda error instead of a clean PENDING. Without the fix for #917, it fails the execution.
### Possible Implementation
1. Add `TerminationReason.EXECUTION_SUSPENDED_BY_SERVICE` (name open) and classify it `"suspend"` in `TERMINATION_CLASS`. `withDurableExecution` then returns `{ Status: PENDING }` through the existing suspend path with no further change.
2. In `CheckpointManager`, when a checkpoint response has no `CheckpointToken`, call `terminationManager.terminate({ reason: ... })` and stop draining the checkpoint queue. There is no token to send, so any further checkpoint is guaranteed to fail.
3. In-flight operations in the invocation are abandoned, not checkpointed. On the next invocation they replay. This is correct for AT_LEAST_ONCE steps. AT_MOST_ONCE steps whose START landed in the last accepted checkpoint will not run again, which is the defined semantics of AT_MOST_ONCE.
4. Unit test: a checkpoint response without `CheckpointToken` produces `Status: PENDING`, no further checkpoint calls, and no thrown error.
5. Testing SDK: the local checkpoint server needs a way to omit the token on a chosen checkpoint so customers can test their handlers against this path.
### Is this a breaking change?
No. The SDK's response set is unchanged; a new internal termination reason is added.
### Additional Context
- Prerequisite: #917 (message-case bug in stale-token classification). That fix is the fallback path whenever the missing token is not detected, so it should land first.
- Sibling issues in the Python and Java SDKs are linked in a comment below.
Contributor guide
Research direction
Start in src/utils/checkpoint/checkpoint-manager.ts and trace how checkpoint responses reach the termination manager and existing suspend path. Review the checkpoint and termination tests, then add coverage for a response without CheckpointToken: the invocation should return Status: PENDING, make no further checkpoint calls, and throw no error. Check the local checkpoint server support needed to exercise this response in the Testing SDK.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend, testing
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100