Skip to content

[Testing]: Preserve empty callback error history without changing runtime errors #862

Open

Description

@zhongkechen

Split from PR #795 — match replay state to the service, reference commit f8ac9b4; baseline main is d7ae95571c44b18c2a2efdef9a308b1c916c5049. This issue owns one independently implementable slice of that PR. Start a focused branch from current main and port only this slice, rather than stacking on the full PR.

Problem and reproduction

The local runner conflates two representations: detailed history may contain a present empty Error.Payload object, while SDK-facing operation state should treat an empty error object as absent diagnostics. EventError.from_dict currently drops a present empty payload by testing its truthiness. A live callback completed with an empty ErrorObject can also behave differently in memory versus a filesystem serialization round-trip.

Through the public runner/executor callback API, complete a real callback with no diagnostic fields. Inspect detailed history, history with IncludeExecutionData=False, live operation state, persisted/reloaded state, and operations reconstructed from history. Repeat with an explicit empty ErrorObject and a nonempty error control.

Scope and implementation

  • Preserve the difference between omitted/null Payload and present Payload={} in EventError.from_dict.
  • Normalize an empty live callback failure error to absent SDK diagnostics at the completion boundary.
  • Project an empty payload for detailed callback-failure history without mutating live state or inventing message/type/stack fields.
  • Normalize empty errors when converting failure history back into callback, step, child-context, and chained-invoke operation state; preserve meaningful error objects.
  • Keep main's IncludeExecutionData behavior: omitted data must not leak payloads, and Truncated follows that inclusion flag.

Primary code: Execution.complete_callback_failure in execution.py, plus EventError.from_dict, Event.create_callback_event_failed, and events_to_operations in model.py.

Acceptance criteria

  • Omitted/null/empty/nonempty payloads round-trip with their distinct meanings and truncation flags preserved.
  • An actual empty-error callback completion produces equivalent SDK state in memory and filesystem stores, both before and after persistence.
  • Detailed callback-failure history retains Payload: {}; nondetailed history omits the payload. Runtime state remains FAILED with absent diagnostic details.
  • A public handler observes the stored failure on resume without repeating completed user code. Nonempty error message/type/data/stack content remains intact.
  • History-to-operation reconstruction preserves the same empty-error semantics for callback, step, context, and chained-invoke failures.
  • Timeout behavior is unchanged: no synthetic empty failure payload is added to a timeout, and detailed timeout history preserves main's truncation behavior.

Reference tests and missing coverage

The empty-payload case of test_event_error_roundtrip_preserves_present_payload failed against baseline source during #795 validation. Preserve the expectation updates in test_callback_failure_history_projection_preserves_sdk_state and test_callback_timeout_history_projection_is_unchanged that retain main's data-inclusion rules.

Add the live completion regression identified in review. Directly constructing history/operation objects does not exercise the completion-boundary normalization. The new test must fail if that normalization is removed, cover both stores, and inspect both runtime state and history.

Independence

This is independent of callback updated-ID production and input acknowledgment: drive a single callback completion and inspect state/history directly, without asserting cross-invocation update counts. It also requires no replay pruning, flag projection, OTel status change, or core snapshot-copy change. OTel status/exception changes in #853 and plugin error isolation in #852 are separate concerns.

Validation and evidence

Add a regression that fails on main without this fix and passes with this slice alone. Run from the repository root:

hatch run dev-testing:test
hatch run dev-testing:typecheck
hatch run dev-testing:js-examples

Use the repository's CI-pinned Hatch version (1.16.5 at the reference) and Node.js 22+ for the JS harness; run hatch fmt --check from packages/aws-durable-execution-sdk-python-testing. Use public-handler integration tests for cross-component behavior and both memory/filesystem stores where relevant. List changed test expectations and their reasons in the implementing PR.

The service-parity expectations are carried forward from #795. This split does not independently verify them against deployed AWS Lambda; cite service evidence or mark the external assumption unverified before claiming deployed-service qualification. Shared conformance work belongs to #796 / conformance PR #131; coordinate new requirements there.

Related issues in this split

Activity

  1. added
    pkg:testingPackage: aws-durable-execution-sdk-python-testing
    and removed on Oct 28, 2026
  2. changed the title [-][Testing]: Preserve empty callback error history without changing runtime errors[/-] [+][SDK/Testing]: Preserve empty errors through deserialization and history[/+] on Oct 28, 2026
  3. changed the title [-][SDK/Testing]: Preserve empty errors through deserialization and history[/-] [+][Testing]: Preserve empty callback error history without changing runtime errors[/+] on Oct 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugpkg:testingPackage: aws-durable-execution-sdk-python-testing

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions