mastra-ai/mastra · error
save-workflow requires a Mastra context.
Error message
save-workflow requires a Mastra context.
What it means
save-workflow is a sub-agent tool whose execute() only works when the tool runtime provides a Mastra instance via the tool execution context. The library throws this error when the context is missing (mastra is undefined) because persisting a Dynamic Workflow via mastra.addDynamicWorkflow() is impossible without a live Mastra instance to register it on. It is a deliberate guard so the tool fails fast with a clear message instead of an opaque TypeError on a missing dependency.
Source
Thrown at mastracode/sdk/src/tools/workflows/save-workflow.ts:32
export { WORKFLOW_BUILDER_MAPPING_CONFIG_DESCRIPTION as MAPPING_CONFIG_DESCRIPTION } from '@mastra/core/workflows/builder';
export const workflowDefinitionInputSchema = z.preprocess(
normalizeWorkflowBuilderDefinition,
workflowBuilderDefinitionSchema,
);
export const saveWorkflowTool = createTool({
id: 'save-workflow',
description:
'Persist a Dynamic Workflow definition and live-register it on the running Mastra instance. Supports all ten persisted graph families: agent, tool, mapping, nested workflow, parallel, foreach, sleep, sleepUntil, conditional, and loop. Conditional and loop entries require declarative predicates; JS closures cannot round-trip through storage. After this returns, the workflow is immediately runnable. Call it exactly once with the complete definition; there is no incremental save API.',
inputSchema: workflowDefinitionInputSchema,
outputSchema: z.object({
ok: z.literal(true),
id: z.string(),
}),
execute: async (def, { mastra }) => {
if (!mastra) throw new Error('save-workflow requires a Mastra context.');
const m = mastra as Mastra;
const normalizedDefinition = normalizeWorkflowBuilderDefinition(def);
// `mastra.addDynamicWorkflow` performs registry pre-flight — a mis-classified
// agentId/toolId or unregistered id throws before rehydration with an
// actionable message listing every offender. It also rejects JSON Schemas
// that use keywords the storage-side converter can't rehydrate
// (oneOf/anyOf/allOf/not/$ref/patternProperties/discriminator).
await m.addDynamicWorkflow(normalizedDefinition as Parameters<Mastra['addDynamicWorkflow']>[0]);
return { ok: true as const, id: normalizedDefinition.id };
},
});
View on GitHub (pinned to 75dd419e61)
Solutions
- Run the tool through a Mastra agent/workflow execution path so the runtime injects the mastra context.
- If invoking execute() directly, pass a context object with a mastra instance: execute(def, { mastra: new Mastra() }) or your existing instance.
- If using the tool in tests, create a Mastra instance in the test setup and supply it in the execution context instead of omitting the second argument.
- Verify the mastracode SDK version wires the tool's ExecutionContext with mastra; upgrade if the runner strips it.
Example fix
// before
const result = await saveWorkflowTool.execute(definition, {});
// after
import { Mastra } from '@mastra/core/mastra';
const mastra = new Mastra({ /* agents, workflows, storage */ });
const result = await saveWorkflowTool.execute(definition, { mastra }); Defensive patterns
Strategy: validation
Validate before calling
function assertMastraContext(ctx) {
if (!ctx || !ctx.mastra) {
throw new Error('save-workflow requires a Mastra context: invoke the tool via a Mastra agent/workflow runner.');
}
return ctx;
}
const result = await saveWorkflowTool.execute(def, assertMastraContext(execCtx)); Type guard
function hasMastra(ctx) {
return typeof ctx === 'object' && ctx !== null && 'mastra' in ctx && ctx.mastra != null;
} Try / catch
try {
await saveWorkflowTool.execute(def, { mastra });
} catch (err) {
if (err instanceof Error && err.message.includes('requires a Mastra context')) {
// fix wiring: re-run through a Mastra-managed execution path
} else throw err;
} Prevention
- Always run SDK tools through Mastra's agent/workflow execution so the context (including mastra) is injected.
- In tests, build a Mastra instance in beforeAll and pass it in the tool execution context.
- Never hand-roll tool runners that omit the ExecutionContext's mastra field.
- Add an assertion/helper at the call boundary that fails fast with a descriptive message when mastra is missing.
When it happens
Trigger: Calling saveWorkflowTool (id 'save-workflow') outside a Mastra-managed tool execution context, e.g. invoking execute() manually with no second argument, registering the tool in an agent/server that does not pass a Mastra instance into tool execution context, or running it in a harness/test that stubs the context without the mastra field.
Common situations: Direct unit-test invocation of the tool's execute function without wiring up a Mastra instance; embedding the tool in a custom runner that skips Mastra's tool-context injection; calling the tool from a code path that bypasses the agent/loop plumbing that normally provides { mastra }.
Related errors
- @mastra/livekit: `workflowInput` is required when `workflow`
- list-available-agents requires a Mastra context.
- list-available-tools requires a Mastra context.
- list-available-workflows requires a Mastra context.
- list-workflows requires a Mastra context.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/e3d649812dc83440.
Report an issue: GitHub.