Skip to content

Commit a0dc691

Browse files
committed
fix(sdk): serialize context Error and ReplayChildren
Operation.to_dict dropped Error and ReplayChildren from ContextDetails and emitted only Result. The read path (ContextDetails.from_dict) and the checkpoint update serializer already handle both fields, so serialized operation state did not round trip through Operation.to_dict. Include Error and ReplayChildren when present. A serialized child context failure now reconstructs the correct error class on replay, and a large payload child context keeps its ReplayChildren marker. The impact is confined to code that serializes operation state to feed a handler, which the local test runner does. Normal published SDK execution does not call Operation.to_dict on this path, so core runtime behavior for callers is unchanged. Closes #477
1 parent 12b5d66 commit a0dc691

2 files changed

Lines changed: 47 additions & 1 deletion

File tree

packages/aws-durable-execution-sdk-python/src/aws_durable_execution_sdk_python/lambda_service.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -914,7 +914,14 @@ def to_dict(self) -> MutableMapping[str, Any]:
914914
"InputPayload": self.execution_details.input_payload
915915
}
916916
if self.context_details:
917-
result["ContextDetails"] = {"Result": self.context_details.result}
917+
context_dict: MutableMapping[str, Any] = {
918+
"Result": self.context_details.result,
919+
}
920+
if self.context_details.error:
921+
context_dict["Error"] = self.context_details.error.to_dict()
922+
if self.context_details.replay_children:
923+
context_dict["ReplayChildren"] = self.context_details.replay_children
924+
result["ContextDetails"] = context_dict
918925
if self.step_details:
919926
step_dict: MutableMapping[str, Any] = {"Attempt": self.step_details.attempt}
920927
if self.step_details.next_attempt_timestamp:

packages/aws-durable-execution-sdk-python/tests/lambda_service_test.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2843,3 +2843,42 @@ def test_timestamp_converter_millisecond_boundaries():
28432843

28442844

28452845
# endregion
2846+
2847+
2848+
def test_operation_to_dict_serializes_context_error_and_replay_children():
2849+
"""Operation.to_dict includes Error and ReplayChildren in ContextDetails."""
2850+
op = Operation(
2851+
operation_id="ctx-1",
2852+
operation_type=OperationType.CONTEXT,
2853+
status=OperationStatus.FAILED,
2854+
context_details=ContextDetails(
2855+
replay_children=True,
2856+
result=None,
2857+
error=ErrorObject(
2858+
message="boom",
2859+
type="ChildContextError",
2860+
data=None,
2861+
stack_trace=None,
2862+
),
2863+
),
2864+
)
2865+
2866+
context = op.to_dict()["ContextDetails"]
2867+
2868+
assert context["Error"]["ErrorType"] == "ChildContextError"
2869+
assert context["Error"]["ErrorMessage"] == "boom"
2870+
assert context["ReplayChildren"] is True
2871+
2872+
2873+
def test_operation_to_dict_omits_absent_context_error_and_replay_children():
2874+
"""Operation.to_dict omits Error and ReplayChildren when they are not set."""
2875+
op = Operation(
2876+
operation_id="ctx-2",
2877+
operation_type=OperationType.CONTEXT,
2878+
status=OperationStatus.SUCCEEDED,
2879+
context_details=ContextDetails(result='"hello"'),
2880+
)
2881+
2882+
context = op.to_dict()["ContextDetails"]
2883+
2884+
assert context == {"Result": '"hello"'}

0 commit comments

Comments
 (0)