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

Fields

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions