n8n-io/n8n · error · Error
Tool "${this.name}" requires a description
Error message
Tool "${this.name}" requires a description What it means
Tool.build() requires a description because the LLM uses it to decide when and how to invoke the tool. Without a description, the model cannot reason about the tool's purpose, making it effectively invisible to the agent loop. The description must be set via .description() before build.
Source
Thrown at packages/@n8n/agents/src/sdk/tool.ts:361
* Example: `.providerOptions({ anthropic: { eagerInputStreaming: true } })`
*/
providerOptions(options: Record<string, JSONObject>): this {
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()`);
}
View on GitHub (pinned to 5ac6606e81)
Solutions
- Add .description('...') to the builder chain describing what the tool does and when to use it.
- Write descriptions aimed at the LLM consumer: state inputs, purpose, and notable side effects.
- For tools generated from metadata, assert a description field exists in the source metadata before constructing the Tool.
Example fix
// before
const t = new Tool('get_weather').input(z.object({...})).handler(...);
agent.tool(t); // throws: requires a description
// after
const t = new Tool('get_weather')
.description('Fetch current weather for a city')
.input(z.object({ city: z.string() }))
.handler(...); Defensive patterns
Strategy: validation
Validate before calling
function toolWithDescription(name: string, desc: string | undefined) {
if (!desc || desc.trim().length === 0) {
throw new Error(`Tool ${name} requires a non-empty description`);
}
return new Tool(name).description(desc);
} Type guard
function hasDescription(desc: unknown): desc is string {
return typeof desc === 'string' && desc.trim().length > 0;
} Prevention
- Make description a required field in any tool-definition config schema.
- Write descriptions for the LLM reader, not for the developer — state purpose, inputs, side effects.
- Add a lint/check in your tool factory that asserts .description() was called.
When it happens
Trigger: Calling new Tool('name') without chaining .description(...), then building (directly or via agent.tool(...)). The check fires only after the name check passes, so the error message includes the tool's name.
Common situations: Forgetting the .description() call in a builder chain; assuming the name doubles as the description; stripping a builder chain down during a refactor and dropping the line.
Related errors
- Tool name is required
- Tool "${this.name}" requires an input schema
- 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/58fc1f28a639db53.
Report an issue: GitHub.