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
- Define both inputSchema and outputSchema on the tool (z.object(...) or any Standard Schema).
- If the tool genuinely has no input/output, use z.object({}) rather than leaving the fields undefined.
- 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
- Type tool definitions with zod schemas on both input and output at authoring time.
- Use z.object({}) for empty input/output instead of omitting the fields.
- Add a unit test that creates steps from every registered tool at startup.
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
- Workflow definition graph must be an array.
- WORKFLOW_SCHEMA_VALIDATION_FAILED
- Request context validation failed for workflow '${this.workf
- @mastra/livekit: `workflowInput` is required when `workflow`
- ${label} contains an unsupported field.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/1de552c42cf94894.
Report an issue: GitHub.