Data retention
A finished run leaves data behind: the inputs and outputs of the workflow and each of its steps, the payloads on its event log, and anything written to its streams. How long that data is kept is decided by the World you are running on, not by the SDK.
experimental_retention on start()
lets a run ask for a specific retention period, rather than the World's default.
Deleting a run's data as soon as it ends
0 asks the World to delete the run's user data the moment the run
completes or fails, rather than keeping it for the World's default window.
Two values are accepted today:
| Value | Meaning |
|---|---|
0 | Delete user data as soon as the run reaches a terminal state. |
'default' | Use the World's default. Identical to omitting the option. |
The option is prefixed experimental_ because both its name and the set of
values it accepts are expected to change.
What is deleted, and what is not
Deleted: the run's input, output and error; every step's input, output and error; the payloads on the event log; and stream contents.
Kept: the run, step and event records themselves — their ids, timestamps, status, step names, and any attributes you set. They are kept for the World's default period so the run stays visible in the CLI and web UI. A purged run is still listed and still traceable; its payloads simply read back as expired.
Inspecting a purged run shows it as expired rather than failing. The Workflow
CLI renders the run's own input, output and error as <data expired>:
Step, hook and event payloads read back empty. On the Vercel World they also
render as <data expired>; on Worlds that clear the stored value outright
they simply show as empty. Either way the data is gone — the difference is
only in how the absence is labelled.
You cannot read the return value of a run started with
experimental_retention: 0. The deletion races your own read of the
result and generally wins, so await run.returnValue throws
RunExpiredError instead of resolving.
This is a known limitation. If you need the result, send it somewhere you control, e.g. a step that writes it to your own store, rather than reading it back off the run.
RunExpiredError is not specific to experimental_retention: 0. Any run read
after its retention window has passed throws it, and the error carries
runId, runStatus and expiredAt when the World still has them — so a
caller can tell a successful run whose result is gone from a failed one whose
error is gone. If the run's metadata is gone too, the World reports the run as
missing and you get WorkflowRunNotFoundError instead.
Retention is implemented by the World
The SDK records your preference; it does not enforce it. start() writes the
value onto the run as the reserved $retention attribute, and the World
decides what to do when the run ends. Attributes are a spec version 4 feature,
so experimental_retention: 0 throws against an older World rather than being
recorded and ignored. A World that does implement spec version 4 but not
retention keeps the data. If you need certainty that a specific World
deletes your data, confirm it against that World's own documentation rather
than the presence of this option.