@mastra/core
Version:
Mastra is a framework for building AI-powered applications and agents with a modern TypeScript stack.
232 lines (179 loc) • 8.12 kB
Markdown
# Suspend & resume
Workflows can be paused at any step to collect additional data, wait for API callbacks, throttle costly operations, or request [human-in-the-loop](https://mastra.ai/docs/workflows/human-in-the-loop) input. When a workflow is suspended, its current execution state is saved as a snapshot. You can later resume the workflow from a [specific step ID](https://mastra.ai/docs/workflows/snapshots), restoring the exact state captured in that snapshot. [Snapshots](https://mastra.ai/docs/workflows/snapshots) are stored in your configured storage provider and persist across deployments and application restarts.
## Pausing a workflow with `suspend()`
Use `suspend()` to pause workflow execution at a specific step. You can define a suspend condition in the step’s `execute` block using values from `resumeData`.
- If the condition isn’t met, the workflow pauses and returns `suspend()`.
- If the condition is met, the workflow continues with the remaining logic in the step.

```typescript
const step1 = createStep({
id: 'step-1',
inputSchema: z.object({
userEmail: z.string(),
}),
outputSchema: z.object({
output: z.string(),
}),
resumeSchema: z.object({
approved: z.boolean(),
}),
execute: async ({ inputData, resumeData, suspend }) => {
const { userEmail } = inputData
const { approved } = resumeData ?? {}
if (!approved) {
return await suspend({})
}
return {
output: `Email sent to ${userEmail}`,
}
},
})
export const testWorkflow = createWorkflow({
id: 'test-workflow',
inputSchema: z.object({
userEmail: z.string(),
}),
outputSchema: z.object({
output: z.string(),
}),
})
.then(step1)
.commit()
```
## Restarting a workflow with `resume()`
Use `resume()` to restart a suspended workflow from the step where it paused. Pass `resumeData` matching the step's `resumeSchema` to satisfy the suspend condition and continue execution.

```typescript
import { step1 } from './workflows/test-workflow'
const workflow = mastra.getWorkflow('testWorkflow')
const run = await workflow.createRun()
await run.start({
inputData: {
userEmail: 'alex@example.com',
},
})
const handleResume = async () => {
const result = await run.resume({
step: step1,
resumeData: { approved: true },
})
}
```
Passing the `step` object provides full type-safety for `resumeData`. Alternatively, you can pass a step ID for more flexibility when the ID comes from user input or a database.
```typescript
const result = await run.resume({
step: 'step-1',
resumeData: { approved: true },
})
```
If only one step is suspended, you can omit the step argument entirely and Mastra will resume the last suspended step in the workflow.
When resuming with only a `runId`, create a run instance first using `createRun()`.
```typescript
const workflow = mastra.getWorkflow('testWorkflow')
const run = await workflow.createRun({ runId: '123' })
const stream = run.resume({
resumeData: { approved: true },
})
```
You can call `resume()` from anywhere in your application, including HTTP endpoints, event handlers, in response to [human input](https://mastra.ai/docs/workflows/human-in-the-loop), or timers.
```typescript
const midnight = new Date()
midnight.setUTCHours(24, 0, 0, 0)
setTimeout(async () => {
await run.resume({
step: 'step-1',
resumeData: { approved: true },
})
}, midnight.getTime() - Date.now())
```
## Accessing suspend data with `suspendData`
When a step is suspended, you may want to access the data that was provided to `suspend()` when the step is later resumed. Use the `suspendData` parameter in your step's execute function to access this data.
```typescript
const approvalStep = createStep({
id: 'user-approval',
inputSchema: z.object({
requestId: z.string(),
}),
resumeSchema: z.object({
approved: z.boolean(),
}),
suspendSchema: z.object({
reason: z.string(),
requestDetails: z.string(),
}),
outputSchema: z.object({
result: z.string(),
}),
execute: async ({ inputData, resumeData, suspend, suspendData }) => {
const { requestId } = inputData
const { approved } = resumeData ?? {}
// On first execution, suspend with context
if (!approved) {
return await suspend({
reason: 'User approval required',
requestDetails: `Request ${requestId} pending review`,
})
}
// On resume, access the original suspend data
const suspendReason = suspendData?.reason || 'Unknown'
const details = suspendData?.requestDetails || 'No details'
return {
result: `${details} - ${suspendReason} - Decision: ${approved ? 'Approved' : 'Rejected'}`,
}
},
})
```
The `suspendData` parameter is automatically populated when a step is resumed and contains the exact data that was passed to the `suspend()` function during the original suspension. This allows you to maintain context about why the workflow was suspended and use that information during the resume process.
## Identifying suspended executions
When a workflow is suspended, it restarts from the step where it paused. You can check the workflow's `status` to confirm it's suspended, and use `suspended` to identify the paused step or [nested workflow](https://mastra.ai/docs/workflows/overview).
```typescript
const workflow = mastra.getWorkflow('testWorkflow')
const run = await workflow.createRun()
const result = await run.start({
inputData: {
userEmail: 'alex@example.com',
},
})
if (result.status === 'suspended') {
console.log(result.suspended[0])
await run.resume({
step: result.suspended[0],
resumeData: { approved: true },
})
}
```
### Example output
The `suspended` array contains the IDs of any suspended workflows and steps from the run. These can be passed to the `step` parameter when calling `resume()` to target and resume the suspended execution path.
```typescript
['nested-workflow', 'step-1']
```
## Recovering suspended runs
Use `workflow.getWorkflowRunById()` with `createWorkflowStateReader()` when your application needs to recover a suspended run from storage. The reader exposes suspended steps, resume labels, step payloads, and step outputs without reading the raw snapshot shape.
```typescript
import { createWorkflowStateReader } from '@mastra/core/workflows'
const workflow = mastra.getWorkflow('testWorkflow')
const state = await workflow.getWorkflowRunById('run-123')
if (state?.status === 'suspended') {
const reader = createWorkflowStateReader(state)
const suspendedStep = reader.getSuspendedStep()
const approvalLabel = reader.getResumeLabel('approve')
const run = await workflow.createRun({ runId: state.runId })
await run.resume({
step: approvalLabel?.stepId ?? suspendedStep?.path,
resumeData: { approved: true },
forEachIndex: approvalLabel?.foreachIndex,
})
}
```
For nested workflows, `suspendedStep.path` contains the resume path. For `foreach` suspensions, matching resume labels include `foreachIndex` when the label points to a specific iteration.
## Sleep
Sleep methods can be used to pause execution at the workflow level, which sets the status to `waiting`. By comparison, `suspend()` pauses execution within a specific step and sets the status to `suspended`.
**Available methods:**
- [`.sleep()`](https://mastra.ai/reference/workflows/workflow-methods/sleep): Pause for a specified number of milliseconds
- [`.sleepUntil()`](https://mastra.ai/reference/workflows/workflow-methods/sleepUntil): Pause until a specific date
## Related
- [Control Flow](https://mastra.ai/docs/workflows/control-flow)
- [Human-in-the-loop](https://mastra.ai/docs/workflows/human-in-the-loop)
- [Snapshots](https://mastra.ai/docs/workflows/snapshots)
- [Time Travel](https://mastra.ai/docs/workflows/time-travel)
- [Workflow state reader](https://mastra.ai/reference/workflows/workflow-state-reader)