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 result output.
const step1 = createStep({
id: "step-1",
description: "Test suspend",
inputSchema: z.object({
input: z.string()
}),
outputSchema: z.object({
output: z.string()
}),
suspendSchema: z.object({}),
resumeSchema: z.object({
city: z.string()
}),
execute: async ({ resumeData, suspend }) => {
if (!(resumeData ?? {}).city) {
await suspend({});
return { output: "" };
}
return {
output: ""
};
}
});
export const testWorkflow = createWorkflow({})
.then(step1)
.commit();
See Define Suspendable workflow for more information.
Identifying Suspended steps
To resume a suspended workflow, inspect the suspended
array in the result to determine which step needs input:
import { mastra } from "./mastra";
const run = await mastra.getWorkflow("testWorkflow").createRunAsync();
const result = await run.start({
inputData: {
city: "London"
}
});
console.log(JSON.stringify(result, null, 2));
if (result.status === "suspended") {
const resumedResult = await run.resume({
step: result.suspended[0],
resumeData: {
city: "Berlin"
}
});
}
In this case, the logic resumes the first step listed in the suspended
array. A step
can also be defined using it’s id
, for example: ‘step-1’.
{
"status": "suspended",
"steps": {
// ...
"step-1": {
// ...
"status": "suspended",
}
},
"suspended": [
[
"step-1"
]
]
}
See Run Workflow Results for more details.
Resume
A workflow can be resumed by calling resume
and providing the required resumeData
.
import { mastra } from "./mastra";
const run = await mastra.getWorkflow("testWorkflow").createRunAsync();
const result = await run.start({
inputData: {
city: "London"
}
});
console.log(JSON.stringify(result, null, 2));
if (result.status === "suspended") {
const resumedResult = await run.resume({
step: 'step-1',
resumeData: {
city: "Berlin"
}
});
console.log(JSON.stringify(resumedResult, null, 2));
}
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 = await dowhileWorkflow.createRunAsync();
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 = await mastra.getWorkflow("testWorkflow").createRunAsync();
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
});
}