mastra-ai/mastra · error · Error

Tool must have input and output schemas defined

Error message

Tool must have input and output schemas defined

What it means

createStepFromTool requires the ToolStep to declare both inputSchema and outputSchema so the workflow can validate data flowing in and out of the step. If either schema is missing, this Error is thrown before the step is registered.

Source

Thrown at packages/core/src/workflows/evented/workflow.ts:661

      }

      return {
        text: await textPromise,
      } as TStepOutput;
    },
    component: 'AGENT',
  };
}

function createStepFromTool<TStepInput, TSuspend, TResume, TStepOutput>(
  params: ToolStep<TStepInput, TSuspend, TResume, TStepOutput, any>,
  agentOrToolOptions?: Record<string, unknown>,
): Step<string, any, TStepInput, TStepOutput, TResume, TSuspend, DefaultEngineType> {
  const toolOpts = agentOrToolOptions as
    | { retries?: number; scorers?: DynamicArgument<MastraScorers>; metadata?: StepMetadata }
    | undefined;
  if (!params.inputSchema || !params.outputSchema) {
    throw new Error('Tool must have input and output schemas defined');
  }

  return {
    id: params.id,
    description: params.description,
    inputSchema: params.inputSchema,
    outputSchema: params.outputSchema,
    resumeSchema: params.resumeSchema,
    suspendSchema: params.suspendSchema,
    retries: toolOpts?.retries,
    scorers: toolOpts?.scorers,
    metadata: toolOpts?.metadata,
    execute: async ({
      inputData,
      mastra,
      requestContext,
      suspend,
      resumeData,

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Define both inputSchema and outputSchema on the tool (z.object(...) or any Standard Schema).
  2. If the tool genuinely has no input/output, use z.object({}) rather than leaving the fields undefined.
  3. Confirm the object passed is the full tool (not a destructured subset like just its execute function).

Example fix

// before
const tool = { id: 'weather', execute: async ({ context }) => getWeather(context.city) };
createStep(tool);

// after
const tool = {
  id: 'weather',
  inputSchema: z.object({ city: z.string() }),
  outputSchema: z.object({ temp: z.number() }),
  execute: async ({ context }) => ({ temp: await getWeather(context.city) }),
};
createStep(tool);
Defensive patterns

Strategy: validation

Validate before calling

function assertToolSchemas(tool: { id: string; inputSchema?: unknown; outputSchema?: unknown }) {
  if (!tool.inputSchema || !tool.outputSchema) {
    throw new Error(`Tool ${tool.id} must define inputSchema and outputSchema before createStep(tool)`);
  }
}

Type guard

const hasSchemas = (t: any): t is { inputSchema: object; outputSchema: object } =>
  t?.inputSchema != null && t?.outputSchema != null;

Try / catch

try {
  return createStep(tool);
} catch (e) {
  if (e instanceof Error && e.message === 'Tool must have input and output schemas defined') {
    throw new Error(`Tool '${(tool as any)?.id}' is missing schemas`);
  }
  throw e;
}

Prevention

When it happens

Trigger: createStep(tool) where tool.inputSchema or tool.outputSchema is undefined — e.g. a tool created without schemas, a tool object spread losing schema fields, or a dynamically-built tool whose schema assignment failed.

Common situations: Tools written for other frameworks that only define execute; building tools conditionally at runtime and forgetting schemas; TypeScript types relaxed to any hiding the missing fields.

Related errors


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