Skip to main content
Mastra v1 is coming in January 2026. Get ahead by starting new projects with the beta or upgrade your existing project today.

Workflow.until()

The .until() method repeats a step until a specified condition becomes true. This creates a loop that continues executing the specified step until the condition is satisfied.

UsageDirect link to Usage

workflow.step(incrementStep).until(condition, incrementStep).then(finalStep);

ParametersDirect link to Parameters

condition:

Function | ReferenceCondition
A function or reference condition that determines when to stop looping

step:

Step
The step to repeat until the condition is met

Condition TypesDirect link to Condition Types

Function ConditionDirect link to Function Condition

You can use a function that returns a boolean:

workflow
.step(incrementStep)
.until(async ({ context }) => {
const result = context.getStepResult<{ value: number }>("increment");
return (result?.value ?? 0) >= 10; // Stop when value reaches or exceeds 10
}, incrementStep)
.then(finalStep);

Reference ConditionDirect link to Reference Condition

You can use a reference-based condition with comparison operators:

workflow
.step(incrementStep)
.until(
{
ref: { step: incrementStep, path: "value" },
query: { $gte: 10 }, // Stop when value is greater than or equal to 10
},
incrementStep,
)
.then(finalStep);

Comparison OperatorsDirect link to Comparison Operators

When using reference-based conditions, you can use these comparison operators:

OperatorDescriptionExample
$eqEqual to{ $eq: 10 }
$neNot equal to{ $ne: 0 }
$gtGreater than{ $gt: 5 }
$gteGreater than or equal to{ $gte: 10 }
$ltLess than{ $lt: 20 }
$lteLess than or equal to{ $lte: 15 }

ReturnsDirect link to Returns

workflow:

Workflow
The workflow instance for chaining

ExampleDirect link to Example

import { LegacyWorkflow, LegacyStep } from "@mastra/core/workflows/legacy";
import { z } from "zod";

// Create a step that increments a counter
const incrementStep = new LegacyStep({
id: "increment",
description: "Increments the counter by 1",
outputSchema: z.object({
value: z.number(),
}),
execute: async ({ context }) => {
// Get current value from previous execution or start at 0
const currentValue =
context.getStepResult<{ value: number }>("increment")?.value ||
context.getStepResult<{ startValue: number }>("trigger")?.startValue ||
0;

// Increment the value
const value = currentValue + 1;
console.log(`Incrementing to ${value}`);

return { value };
},
});

// Create a final step
const finalStep = new LegacyStep({
id: "final",
description: "Final step after loop completes",
execute: async ({ context }) => {
const finalValue = context.getStepResult<{ value: number }>(
"increment",
)?.value;
console.log(`Loop completed with final value: ${finalValue}`);
return { finalValue };
},
});

// Create the workflow
const counterWorkflow = new LegacyWorkflow({
name: "counter-workflow",
triggerSchema: z.object({
startValue: z.number(),
targetValue: z.number(),
}),
});

// Configure the workflow with an until loop
counterWorkflow
.step(incrementStep)
.until(async ({ context }) => {
const targetValue = context.triggerData.targetValue;
const currentValue =
context.getStepResult<{ value: number }>("increment")?.value ?? 0;
return currentValue >= targetValue;
}, incrementStep)
.then(finalStep)
.commit();

// Execute the workflow
const run = counterWorkflow.createRun();
const result = await run.start({
triggerData: { startValue: 0, targetValue: 5 },
});
// Will increment from 0 to 5, then stop and execute finalStep