{"record":{"id":"58fc1f28a639db53","repo":"n8n-io/n8n","slug":"tool-this-name-requires-a-description","errorCode":null,"errorMessage":"Tool \"${this.name}\" requires a description","messagePattern":"Tool \"(.+?)\" requires a description","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/agents/src/sdk/tool.ts","lineNumber":361,"sourceCode":"\t * Example: `.providerOptions({ anthropic: { eagerInputStreaming: true } })`\n\t */\n\tproviderOptions(options: Record<string, JSONObject>): this {\n\t\tthis.providerOptionsValue = { ...this.providerOptionsValue, ...options };\n\t\treturn this;\n\t}\n\n\t/**\n\t * Validate configuration and produce a `BuiltTool`.\n\t *\n\t * @throws if name, description, input schema, or handler is missing.\n\t * @throws if suspend is declared without resume or vice versa.\n\t */\n\tbuild(): BuiltTool {\n\t\tif (!this.name) {\n\t\t\tthrow new Error('Tool name is required');\n\t\t}\n\t\tif (!this.desc) {\n\t\t\tthrow new Error(`Tool \"${this.name}\" requires a description`);\n\t\t}\n\t\tif (!this.inputSchema) {\n\t\t\tthrow new Error(`Tool \"${this.name}\" requires an input schema`);\n\t\t}\n\t\tif (!this.handlerFn) {\n\t\t\tthrow new Error(`Tool \"${this.name}\" requires a handler`);\n\t\t}\n\n\t\tconst hasSuspend = this.suspendSchemaValue !== undefined;\n\t\tconst hasResume = this.resumeSchemaValue !== undefined;\n\n\t\tif (hasSuspend && !hasResume) {\n\t\t\tthrow new Error(`Tool \"${this.name}\" has .suspend() but missing .resume()`);\n\t\t}\n\t\tif (hasResume && !hasSuspend) {\n\t\t\tthrow new Error(`Tool \"${this.name}\" has .resume() but missing .suspend()`);\n\t\t}\n","sourceCodeStart":343,"sourceCodeEnd":379,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/agents/src/sdk/tool.ts#L343-L379","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst t = new Tool('get_weather').input(z.object({...})).handler(...);\nagent.tool(t); // throws: requires a description\n\n// after\nconst t = new Tool('get_weather')\n  .description('Fetch current weather for a city')\n  .input(z.object({ city: z.string() }))\n  .handler(...);","handlingStrategy":"validation","validationCode":"function toolWithDescription(name: string, desc: string | undefined) {\n  if (!desc || desc.trim().length === 0) {\n    throw new Error(`Tool ${name} requires a non-empty description`);\n  }\n  return new Tool(name).description(desc);\n}","typeGuard":"function hasDescription(desc: unknown): desc is string {\n  return typeof desc === 'string' && desc.trim().length > 0;\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["tool","builder","validation","llm"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}