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

  1. Run the tool through a Mastra agent/workflow execution path so the runtime injects the mastra context.
  2. If invoking execute() directly, pass a context object with a mastra instance: execute(def, { mastra: new Mastra() }) or your existing instance.
  3. 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.
  4. 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

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


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/e3d649812dc83440. Report an issue: GitHub.