Skip to content

fix(testing-sdk): align checkpoint validation with the service - #996

Merged
ParidelPooya merged 7 commits into
mainfrom
fix/sdk-testing-checkpoint-validation-263
Oct 14, 2026
Merged

ParidelPooya merged 7 commits into
mainfrom
fix/sdk-testing-checkpoint-validation-263

Conversation

@ParidelPooya

@ParidelPooya ParidelPooya commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Issue Link, if available

Fixes #263

Description

The local checkpoint server validates each CheckpointDurableExecution request. Its checks differed from the service's checks. It accepted some updates that the service rejects. It rejected other updates with different messages. So a test could pass locally for a handler that fails in AWS. This PR makes the local checks match the service.

Model checks: Id, ParentId, Name, Type, Action

  1. The service checks each update against its model before any other check.
  2. The local server already did this for SubType only.
  3. It now does it for every constrained member:
    • Id and ParentId: 1 to 64 characters from [a-zA-Z0-9-_].
    • Name: 1 to 256 printable ASCII characters, [\x20-\x7E]+.
    • Id, Type, and Action are required.
  4. The messages, their order within an update, and the cap of 10 listed errors were recorded from CheckpointDurableExecution in us-east-1.
  5. So a step named café now fails locally with the same ValidationException as in AWS.

The service also checks that Type and Action are known enum values. That check is not reproduced. An unknown value still fails, with Invalid operation type. or Invalid action for the given operation type..

Metadata consistency

An update for an existing operation must not change its Type, SubType, Name, or ParentId. The service rejects such an update with <Field> value in OperationUpdate is inconsistent with existing operation. Check for non-determinism.. The local server did not check this.

Size limits

Limit Before Now
Error object (whole serialized update) 32768 bytes 262144 bytes
STEP and CONTEXT output payload none 262144 bytes
CHAINED_INVOKE input payload none 1048576 bytes
EXECUTION output payload none 6291456 bytes

The local runner invokes the handler synchronously. So the EXECUTION limit is the service's limit for a synchronous invocation. Sizes are UTF-8 bytes.

One consequence affects the oversized-result path:

  1. The SDK checkpoints a result as an EXECUTION update when the result exceeds 6291456 − 50 bytes.
  2. The service rejects an EXECUTION payload over 6291456 bytes.
  3. So only a result in that 50-byte window completes through this path. A larger result fails the checkpoint.

The oversized-result integration test used a result of 6 MiB + 1000 characters. The service would reject it. The test now uses a result inside the window. A new case asserts that a larger result fails the execution.

Actions and options

  • CALLBACK and CHAINED_INVOKE accept only START. The service completes them itself. The local server accepted CANCEL. No SDK code sends CANCEL for these types.
  • A WAIT START requires WaitOptions.
  • A CHAINED_INVOKE START requires ChainedInvokeOptions.

The service also rejects a CHAINED_INVOKE to a function in another account or region. Local function names are not ARNs. So that check is not reproduced.

Messages and HTTP status

  1. The error messages are now the service's. For example, Cannot start a WAIT that already exists. is now Cannot start a WAIT operation that already exists..
  2. The service returns these errors as InvalidParameterValueException with HTTP status 400.
  3. The SDK classifies a 4xx checkpoint failure as unrecoverable for the execution.
  4. The local errors carried no status. So they failed only the invocation, and run() rejected.
  5. They now carry status 400. So the local execution ends FAILED with CheckpointUnrecoverableExecutionError, as in AWS.

Behavior change for users

These checks are stricter. A test that passed only because the local server was more permissive now fails. That test fails in AWS too. A test that matches an old message text must update the text. The CHANGELOG lists each change.

Demo/Screenshots

N/A

Checklist

  • I have filled out every section of the PR template
  • I have thoroughly tested this change

Testing

Unit Tests

Yes. The validator suites cover each new check and each changed message: model constraints, field order, the 10-error cap, metadata consistency, each payload limit at its boundary, UTF-8 byte counting, and the CALLBACK and CHAINED_INVOKE action sets.

Integration Tests

Yes. checkpoint-validation.integration.test.ts runs real handlers through LocalDurableTestRunner. A step name outside printable ASCII, or over 256 characters, fails the execution with the service's message. oversized-execution-result.integration.test.ts covers the EXECUTION payload limit.

Results:

  • @aws/durable-execution-sdk-js-testing: 1234 passed, 3 skipped. Lint passes.
  • @aws/durable-execution-sdk-js-examples (jest and vitest): 267 passed, 3 skipped.
  • @aws/durable-execution-sdk-js-extras: 145 passed.
  • @aws/durable-execution-sdk-js-otel: 457 passed.

The local checkpoint server accepted some updates that the service
rejects, and rejected others with different messages. So a test could
pass locally for a handler that fails in AWS.

- Validate Id, ParentId, Name, Type, and Action against the service
  model, as SubType already was. Messages, their order, and the cap of
  10 errors match the service's responses.
- Reject an update that changes the Type, SubType, Name, or ParentId
  of an existing operation.
- Apply the service's error and payload size limits.
- Accept only START for CALLBACK and CHAINED_INVOKE.
- Require WaitOptions and ChainedInvokeOptions on START.
- Use the service's error messages, with HTTP status 400. So a failed
  validation fails the execution, as it does in AWS.

Fixes #263
@ParidelPooya
ParidelPooya temporarily deployed to ai-pr-review-runtime October 13, 2026 22:05 — with GitHub Actions Inactive
@ParidelPooya
ParidelPooya temporarily deployed to ai-pr-review-runtime October 13, 2026 22:28 — with GitHub Actions Inactive
@ParidelPooya ParidelPooya changed the title fix(sdk-testing): align checkpoint validation with the service fix(testing-sdk): align checkpoint validation with the service Oct 13, 2026
@ParidelPooya ParidelPooya reopened this Oct 13, 2026
@ParidelPooya
ParidelPooya temporarily deployed to ai-pr-review-runtime October 13, 2026 23:23 — with GitHub Actions Inactive
@ParidelPooya
ParidelPooya temporarily deployed to ai-pr-review-runtime October 13, 2026 23:51 — with GitHub Actions Inactive
@github-actions

This comment has been minimized.

The service validates the members of the options structures before any
other check. The local server did not. So a CHAINED_INVOKE without a
FunctionName passed validation, and failed later in the orchestrator
with a plain error instead of an HTTP 400 ValidationException.

Validate ChainedInvokeOptions.FunctionName and TenantId,
WaitOptions.WaitSeconds, StepOptions.NextAttemptDelaySeconds, and the
CallbackOptions timeouts. The messages and their order were recorded
from the service.
@ParidelPooya
ParidelPooya temporarily deployed to ai-pr-review-runtime October 14, 2026 01:46 — with GitHub Actions Inactive
@github-actions

This comment has been minimized.

The service reads the request body before it checks the model. A
member of the wrong JSON type fails the request with HTTP 400
SerializationException. The local server ignored such a member.

Report the service's SerializationException message for the first
member of the wrong type, in the order the SDK writes the body.
Read an integer member as the service does: drop the fractional part,
and clamp to the 32-bit range. The messages and the coercion were
recorded from the service.
@ParidelPooya
ParidelPooya temporarily deployed to ai-pr-review-runtime October 14, 2026 03:56 — with GitHub Actions Inactive
@github-actions

This comment has been minimized.

The serialization check covered only the members that have model
constraints. So a wrong type in Payload, an Error member, or
ContextOptions.ReplayChildren passed locally, and failed in AWS. For
example, a step that throws { name: "X", message: 1 } produces a
numeric ErrorMessage.

Check the complete OperationUpdate wire schema, including nested
structures and the StackTrace list. Move the check to its own module,
and run it before the model check. Members are read in alphabetical
order of their names, as the service reads them. The messages and the
order were recorded from the service.
@ParidelPooya
ParidelPooya temporarily deployed to ai-pr-review-runtime October 14, 2026 14:47 — with GitHub Actions Inactive
@github-actions

This comment has been minimized.

The service reads an integer member without its fractional part, and
then applies that integer. The local server validated the integer but
stored the original number. So context.wait({ seconds: 1.5 }) recorded
and scheduled 1.5 seconds locally, and 1 second in AWS.

Store each update with its integer members converted, so local history
and timers use the service's values. A callback timeout of 0 now means
no timeout, as in the service, so a fractional timeout below 1 second
no longer times the callback out at once.

State the whole-value pattern match with an explicit end assertion.
@ParidelPooya
ParidelPooya temporarily deployed to ai-pr-review-runtime October 14, 2026 19:29 — with GitHub Actions Inactive
@github-actions

Copy link
Copy Markdown

Codex AI review

Found one replay-parity issue in the new wire-type handling; boolean coercion is not reflected in stored context state.

Reviewed commit 2be44452346e035f46c586f7502eb30c8c42cfb2. Workflow run

@ParidelPooya
ParidelPooya merged commit dab7bc6 into main Oct 14, 2026
26 checks passed
@ParidelPooya
ParidelPooya deleted the fix/sdk-testing-checkpoint-validation-263 branch October 14, 2026 21:13

This branch was previously deployed

1 inactive deployment
ai-pr-review-runtime — 2be44452 Deployed Oct 14, 2026 by ParidelPooya via ai-pr-review / Codex review / Generate Codex review #1120
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Align checkpoint validation in the testing library

2 participants