Run.timeTravel()
The .timeTravel() method re-executes a workflow starting from any specific step, using either stored snapshot data or custom context you provide. Use it to debug failed workflows and test individual steps with different inputs. It can also recover from errors without re-running the entire workflow.
Usage exampleUsage example的直接链接
const run = await workflow.createRun()
const result = await run.timeTravel({
step: 'step2',
inputData: { value: 10 },
})
ParametersParameters的直接链接
step:
inputData?:
resumeData?:
initialState?:
context?:
nestedStepsContext?:
requestContext?:
outputWriter?:
tracingContext?:
currentSpan?:
tracingOptions?:
rootSpanName?:
workflow run: '<id>' or agent run: '<id>' name. Use it to tell runs apart in trace lists.metadata?:
requestContextKeys?:
traceId?:
parentSpanId?:
nestUnderParent?:
true together with parentSpanId to attach this run as a child of that span inside the existing trace, for example an LLM judge under the run it scores. Exporters then keep the trace's own name, input, output, and tags. Leave unset when parentSpanId comes from your own tracing system. Ignored without a valid traceId and parentSpanId.tags?:
outputOptions?:
includeState?:
includeResumeLabels?:
ReturnsReturns的直接链接
result:
traceId?:
spanId?:
Extended usage examplesExtended usage examples的直接链接
Time travel with custom contextTime travel with custom context的直接链接
const result = await run.timeTravel({
step: 'step2',
context: {
step1: {
status: 'success',
payload: { value: 0 },
output: { step1Result: 2 },
startedAt: Date.now(),
endedAt: Date.now(),
},
},
})
Time travel to nested workflow stepTime travel to nested workflow step的直接链接
// Using dot notation
const result = await run.timeTravel({
step: 'nestedWorkflow.step3',
inputData: { value: 10 },
})
// Using array of step IDs
const result = await run.timeTravel({
step: ['nestedWorkflow', 'step3'],
inputData: { value: 10 },
})
Time travel with initial stateTime travel with initial state的直接链接
const result = await run.timeTravel({
step: 'step2',
inputData: { value: 10 },
initialState: {
counter: 5,
metadata: { source: 'time-travel' },
},
})
Time travel with nested workflows contextTime travel with nested workflows context的直接链接
const result = await run.timeTravel({
step: 'nestedWorkflow.step3',
context: {
step1: {
status: 'success',
payload: { value: 0 },
output: { step1Result: 2 },
startedAt: Date.now(),
endedAt: Date.now(),
},
nestedWorkflow: {
status: 'running',
payload: { step1Result: 2 },
startedAt: Date.now(),
},
},
nestedStepsContext: {
nestedWorkflow: {
step2: {
status: 'success',
payload: { step1Result: 2 },
output: { step2Result: 3 },
startedAt: Date.now(),
endedAt: Date.now(),
},
},
},
})
Concurrent time travel callsConcurrent time travel calls的直接链接
Only one timeTravel() call can run for a workflow run at a time. Before it runs anything, timeTravel() atomically claims the run by moving its stored status from the status it loaded (for example suspended or success) to running. If another caller already claimed it, this call throws WORKFLOW_TIME_TRAVEL_ALREADY_CLAIMED and executes no steps. A call that loads the run while it's already running throws WORKFLOW_TIME_TRAVEL_RUN_RUNNING.
This matters whenever time travel can be triggered more than once for the same run, such as a "retry from step" button pressed twice or a retried API request.
try {
await run.timeTravel({ step: 'step2' })
} catch (error) {
if (
error.id === 'WORKFLOW_TIME_TRAVEL_ALREADY_CLAIMED' ||
error.id === 'WORKFLOW_TIME_TRAVEL_RUN_RUNNING'
) {
// Another caller is already re-running this workflow. Re-read the run
// state instead of time travelling again.
}
}
Over HTTP, POST /api/workflows/:workflowId/time-travel-async returns 409 Conflict for a losing call. The time-travel and time-travel-stream endpoints respond before the time travel finishes, so a losing call doesn't change their status code.
Calling timeTravel() again after the previous call finished is still allowed and re-runs the target step.
The claim is enforced atomically by storage adapters that report supportsConcurrentUpdates(). Adapters without atomic read-modify-write support (such as ClickHouse, Cloudflare D1, Cloudflare KV, Cloudflare Durable Objects, LanceDB, and Redis) can't enforce it, and the claim is also skipped when shouldPersistSnapshot excludes the running status. The Inngest workflow runner doesn't claim the run yet.
NotesNotes的直接链接
- Time travel requires storage to be configured since it relies on persisted workflow snapshots
- When re-executing a workflow, the workflow loads the existing snapshot from storage (if available)
- Step results before the target step are reconstructed from the snapshot or provided context
- Execution begins from the specified step with the provided or reconstructed input data
- If a step that precedes the target in the current workflow definition has no recorded entry in the snapshot (for example, the step was renamed since the run was recorded, or the run never reached it),
timeTravelrejects with a descriptive error before anything executes and the stored snapshot is left unchanged - The workflow continues to completion from that point forward
- Time travel can be used on workflows that haven't been run yet by providing custom context or input data for the step to start from.