Skip to content

Empty serialized payloads are dropped, causing "" to replay as None #554

Description

@zhongkechen

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:

  1. OperationUpdate.to_dict() includes Payload: "".
  2. 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.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions