n8n-io/n8n · error · Error
Tool "${this.name}" requires an input schema
Error message
Tool "${this.name}" requires an input schema What it means
Tool.build() requires an input schema because the runtime needs it to validate the LLM's tool-call arguments, generate the model-facing JSON schema, and type the handler's first argument. Set it via .input(zodSchema) or .input(jsonSchema). Without it, the agent loop cannot serialize the tool for the model.
Source
Thrown at packages/@n8n/agents/src/sdk/tool.ts:364
this.providerOptionsValue = { ...this.providerOptionsValue, ...options };
return this;
}
/**
* Validate configuration and produce a `BuiltTool`.
*
* @throws if name, description, input schema, or handler is missing.
* @throws if suspend is declared without resume or vice versa.
*/
build(): BuiltTool {
if (!this.name) {
throw new Error('Tool name is required');
}
if (!this.desc) {
throw new Error(`Tool "${this.name}" requires a description`);
}
if (!this.inputSchema) {
throw new Error(`Tool "${this.name}" requires an input schema`);
}
if (!this.handlerFn) {
throw new Error(`Tool "${this.name}" requires a handler`);
}
const hasSuspend = this.suspendSchemaValue !== undefined;
const hasResume = this.resumeSchemaValue !== undefined;
if (hasSuspend && !hasResume) {
throw new Error(`Tool "${this.name}" has .suspend() but missing .resume()`);
}
if (hasResume && !hasSuspend) {
throw new Error(`Tool "${this.name}" has .resume() but missing .suspend()`);
}
const hasApproval =
(this.requireApprovalValue ?? false) || this.needsApprovalFnValue !== undefined;
if (hasApproval && (hasSuspend || hasResume)) {View on GitHub (pinned to 5ac6606e81)
Solutions
- Add .input(z.object({...})) with a Zod schema describing the tool's arguments.
- For tools that take no meaningful input, pass z.object({}).describe('no arguments') rather than skipping .input().
- If loading schemas dynamically, assert the schema is defined before calling .input().
Example fix
// before
const t = new Tool('ping').description('Health check').handler(async () => 'ok');
agent.tool(t); // throws: requires an input schema
// after
const t = new Tool('ping')
.description('Health check')
.input(z.object({}).describe('no arguments'))
.handler(async () => 'ok'); Defensive patterns
Strategy: type-guard
Validate before calling
import { isZodSchema } from '@n8n/agents/utils/zod'; // or local util
function attachInput(tool: Tool, schema: unknown) {
if (!isZodSchema(schema) && !(schema && typeof schema === 'object' && 'type' in schema)) {
throw new Error('Input schema must be a Zod schema or JSON Schema object');
}
return tool.input(schema as any);
} Type guard
function isInputSchema(value: unknown): value is import('zod').ZodType | import('json-schema').JSONSchema7 {
if (!value) return false;
// Zod check
if (typeof value === 'object' && '_def' in value && typeof (value as any)._def === 'object') return true;
// JSON Schema check
if (typeof value === 'object' && 'type' in value) return true;
return false;
} Prevention
- Always pair a handler with its input schema in the same builder block so they aren't separated.
- For no-arg tools, pass z.object({}) explicitly rather than skipping .input().
- When loading schemas from external sources, assert they parse as Zod or JSON Schema before attaching.
When it happens
Trigger: Calling new Tool('name').description('...').handler(...) and omitting .input(...), then building. Also triggered when .input() is called with undefined due to a variable that wasn't set.
Common situations: Forgetting .input() in a quick prototype; passing an undefined schema variable; assuming the handler will accept raw args without schema validation.
Related errors
- Tool name is required
- Tool "${this.name}" requires a description
- Tool "${this.name}" requires a handler
- Tool "${this.name}" has .suspend() but missing .resume()
- Tool "${this.name}" has .resume() but missing .suspend()
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/eadcf646ca9ba9b1.
Report an issue: GitHub.