Expected behavior
An empty serialized string is a valid payload and should remain distinct from
an absent payload:
serialize(result) == "" -> replay deserializes "" -> original result
serialize(result) is None -> replay returns None
Operations using a custom SerDes should therefore return the same value on the
first run and replay when SerDes.serialize() returns "".
Actual behavior
OperationUpdate.to_dict() only includes Payload when the value is truthy:
if self.payload:
result["Payload"] = self.payload
Consequently, payload="" is omitted from the
CheckpointDurableExecution request. The backend records a successful
operation without a result, and replay reconstructs that missing result as
None.
For example, the built-in PassThroughSerDes exposes the problem when an
operation returns an empty string:
First run: ""
Replay: None
The underlying wire behavior can be reproduced directly:
update = OperationUpdate.create_step_succeed(
OperationIdentifier(
"step-1",
OperationSubType.STEP,
None,
"empty-result",
),
payload="",
)
assert update.payload == ""
assert "Payload" in update.to_dict() # currently fails
Affected operations
This is shared OperationUpdate serialization behavior and can affect any
operation that checkpoints a serialized result, including:
step
run_in_child_context (and map/parallel paths using child contexts)
wait_for_condition
Suggested fix
Preserve every payload except None:
if self.payload is not None:
result["Payload"] = self.payload
Add coverage that:
OperationUpdate.to_dict() includes Payload: "".
- An operation using
PassThroughSerDes with an empty-string result returns
"" on both first run and replay.
This issue predates PR #550 but was identified while reviewing that PR's
first-run/replay SerDes consistency changes.
Expected behavior
An empty serialized string is a valid payload and should remain distinct from
an absent payload:
Operations using a custom SerDes should therefore return the same value on the
first run and replay when
SerDes.serialize()returns"".Actual behavior
OperationUpdate.to_dict()only includesPayloadwhen the value is truthy:Consequently,
payload=""is omitted from theCheckpointDurableExecutionrequest. The backend records a successfuloperation without a result, and replay reconstructs that missing result as
None.For example, the built-in
PassThroughSerDesexposes the problem when anoperation returns an empty string:
The underlying wire behavior can be reproduced directly:
Affected operations
This is shared
OperationUpdateserialization behavior and can affect anyoperation that checkpoints a serialized result, including:
steprun_in_child_context(and map/parallel paths using child contexts)wait_for_conditionSuggested fix
Preserve every payload except
None:Add coverage that:
OperationUpdate.to_dict()includesPayload: "".PassThroughSerDeswith an empty-string result returns""on both first run and replay.This issue predates PR #550 but was identified while reviewing that PR's
first-run/replay SerDes consistency changes.