Skip to main content

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.

Usage

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

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 Types

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 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 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 }

Returns

workflow:

Workflow
The workflow instance for chaining

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