run-expired

Error

Run "wrun_..." completed, but its data expired at 2026-08-28T05:02:35.009Z
and is no longer readable.

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.

import { getRun } from "workflow/api"
import { RunExpiredError } from "workflow/errors"

export async function readResult(runId: string) {
  try {
    return await getRun(runId).returnValue
  } catch (error) {
    if (RunExpiredError.is(error)) {
      // `runStatus` is the run's terminal status when the World still has
      // it, so you can tell a successful run whose result is gone from a
      // failed one whose error is gone.
      if (error.runStatus === "completed") {
        // The run succeeded; its result is simply no longer stored.
      }
      return null
    }
    throw error
  }
}

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.

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.