run-expired
Error
Thrown as a RunExpiredError from await run.returnValue.
Why this happens
A run's payloads — its input, output and error, and those of its steps — are kept only for as long as the World's retention policy says. Its metadata — id, status, timestamps — usually outlives them. So a run can be readable as a record while its result is already gone.
Rather than hand back a placeholder that is indistinguishable from a value the
workflow genuinely returned, returnValue throws.
Two ways to reach it:
- The run was started with
experimental_retention: 0. Its data is deleted the moment it reaches a terminal state, and that deletion races your own read of the result — and generally wins. On these runs, expect this error rather than treating it as an edge case. See Data retention. - The run simply aged out. It finished long enough ago that the World's default retention window has passed.
How to respond
RunExpiredError is terminal. Retrying will not bring the data back. Catch it
and use the run's metadata to decide what you want to do.
The error carries runId, runStatus and expiredAt when the World reports
them.
If you need the result of a zero-retention run
Do not read it back off the run. Send it somewhere you control while the run
is still executing — a step that writes it to your own store. That is the
intended pattern for experimental_retention: 0: the point of the option is
that the platform does not keep your data, so the platform cannot also be where
you fetch it from afterwards.
Related
If the run is gone entirely — metadata included — the World reports it as
missing and you get a WorkflowRunNotFoundError instead. That means the
record itself has been cleaned up, not just its payloads.