Suspend & Resume
Workflows can be paused at any step, with their current state persisted as a snapshot in storage. Execution can then be resumed from this saved snapshot when ready. Persisting the snapshot ensures the workflow state is maintained across sessions, deployments, and server restarts, essential for workflows that may remain suspended while awaiting external input or resources.
Common scenarios for suspending workflows include:
- Waiting for human approval or input
- Pausing until external API resources become available
- Collecting additional data needed for later steps
- Rate limiting or throttling expensive operations
- Handling event-driven processes with external triggers
Workflow Status
When running a workflow, its status
can be one of the following:
running
- The workflow is currently runningsuspended
- The workflow is suspendedsuccess
- The workflow has completedfailed
- The workflow has failed
Suspend
When the state is suspended
, you can identify any and all steps that have been suspended by looking at the suspended
array of the workflow.
const step1 = createStep({
id: "step-1",
description: "Test suspend",
inputSchema: z.object({
input: z.array(z.string())
}),
resumeSchema: z.object({
city: z.string()
}),
outputSchema: z.object({
output: z.string()
}),
execute: async ({ resumeData, suspend }) => {
const { city } = resumeData ?? {};
if (!city) {
await suspend({});
return {
outcome: ""
};
}
return {
output: ""
};
}
});
export const testWorkflow = createWorkflow({})
.then(step1)
.commit();
See Define Suspendable workflow for more information.
Resume
A workflow can be resumed by calling resume
and providing the required resumeData
.
import { mastra } from "./mastra";
const run = mastra.getWorkflow("testWorkflow").createRun();
const result = await run.start({
inputData: { suggestions: ["London", "Paris", "New York"] }
});
console.log(JSON.stringify(result, null, 2));
if (result.status === "suspended") {
const resumedResult = await run.resume({
step: 'step-1',
resumeData: { city: "New York" }
});
console.log(JSON.stringify(resumedResult, null, 2));
}
To execute this run from your terminal:
npx tsx src/test-workflow.ts
Nested Workflow
To resume a suspended nested workflow pass the workflow instance to the step
parameter of the resume
function.
const dowhileWorkflow = createWorkflow({
id: 'dowhile-workflow',
inputSchema: z.object({ value: z.number() }),
outputSchema: z.object({ value: z.number() }),
})
.dountil(
createWorkflow({
id: 'simple-resume-workflow',
inputSchema: z.object({ value: z.number() }),
outputSchema: z.object({ value: z.number() }),
steps: [incrementStep, resumeStep],
})
.then(incrementStep)
.then(resumeStep)
.commit(),
async ({ inputData }) => inputData.value >= 10,
)
.then(
createStep({
id: 'final',
inputSchema: z.object({ value: z.number() }),
outputSchema: z.object({ value: z.number() }),
execute: async ({ inputData }) => ({ value: inputData.value }),
}),
)
.commit();
const run = dowhileWorkflow.createRun();
const result = await run.start({ inputData: { value: 0 } });
if (result.status === "suspended") {
const resumedResult = await run.resume({
resumeData: { value: 2 },
step: ['simple-resume-workflow', 'resume'],
});
console.log(JSON.stringify(resumedResult, null, 2));
}
RuntimeContext
When using suspend/resume with RuntimeContext
, you can create the instance yourself, and pass it to the start
and resume
functions.
RuntimeContext
is not automatically shared on a workflow run.
import { RuntimeContext } from "@mastra/core/di";
import { mastra } from "./mastra";
const runtimeContext = new RuntimeContext();
const run = mastra.getWorkflow("testWorkflow").createRun();
const result = await run.start({
inputData: { suggestions: ["London", "Paris", "New York"] },
runtimeContext
});
if (result.status === "suspended") {
const resumedResult = await run.resume({
step: 'step-1',
resumeData: { city: "New York" },
runtimeContext
});
}