{"record":{"id":"65aa127a536ddef3","repo":"siyuan-note/siyuan","slug":"config-description-must-not-be-empty","errorCode":null,"errorMessage":"config.description must not be empty","messagePattern":"config\\.description must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/api_agent.go","lineNumber":93,"sourceCode":"\t\t\t\t\tif name == \"\" {\n\t\t\t\t\t\terr = fmt.Errorf(\"capability name must not be empty\")\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\terr = fmt.Errorf(\"first argument must be a tool name string\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif c := call.Argument(1); isJsValueNotNull(c) {\n\t\t\t\t\tconfigObj := c.ToObject(rt)\n\t\t\t\t\tif configObj != nil {\n\t\t\t\t\t\tif titleValue := configObj.Get(\"title\"); goja.IsString(titleValue) {\n\t\t\t\t\t\t\ttitle = titleValue.String()\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif descriptionValue := configObj.Get(\"description\"); goja.IsString(descriptionValue) {\n\t\t\t\t\t\t\tdescription = strings.TrimSpace(descriptionValue.String())\n\t\t\t\t\t\t\tif description == \"\" {\n\t\t\t\t\t\t\t\terr = fmt.Errorf(\"config.description must not be empty\")\n\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\terr = fmt.Errorf(\"config.description is required and must be a string\")\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif inputSchemaValue := configObj.Get(\"inputSchema\"); isJsValueNotNull(inputSchemaValue) {\n\t\t\t\t\t\t\tif inputSchema, err = jsCapabilitySchemaToGoSchema(rt, inputSchemaValue); err != nil {\n\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\terr = fmt.Errorf(\"config.inputSchema is required\")\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif outputSchemaValue := configObj.Get(\"outputSchema\"); isJsValueNotNull(outputSchemaValue) {\n\t\t\t\t\t\t\tif outputSchema, err = jsCapabilitySchemaToGoSchema(rt, outputSchemaValue); err != nil {\n\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t}","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/plugin/api_agent.go#L75-L111","documentation":"The config object's description property was a string, but it contained only whitespace after trimming. The description is required because it is shown to the AI agent to decide when to invoke the tool; a blank description would make the tool unusable.","triggerScenarios":"registerCapability(name, { description: \"   \", inputSchema: {...} }, handler) or description sourced from an empty/whitespace user field.","commonSituations":"I18n lookup returning whitespace when a translation key is missing; template config with an unfilled description slot; copying a config and clearing the description.","solutions":["Write a concise English description of what the tool does in config.description","Trim-check the description value before registering","Keep descriptions specific so the agent can pick the tool correctly"],"exampleFix":"// before\nplugin.registerCapability(\"search\", { description: \"  \", inputSchema }, handler);\n// after\nplugin.registerCapability(\"search\", { description: \"Search notes in the workspace by keyword\", inputSchema }, handler);","handlingStrategy":"validation","validationCode":"if (typeof config.description === \"string\" && !config.description.trim()) { throw new Error(\"config.description is blank\"); }","typeGuard":"const hasDescription = (c) => typeof c?.description === \"string\" && c.description.trim().length > 0;","tryCatchPattern":"try { plugin.registerCapability(name, config, handler); } catch (e) { if (String(e).includes(\"config.description must not be empty\")) { config.description = name + \" tool\"; return plugin.registerCapability(name, config, handler); } throw e; }","preventionTips":["Always write a meaningful description for every capability","Check i18n lookups used for descriptions return real text","Add a config lint step in plugin build that rejects blank description fields"],"tags":["plugin-api","empty-string","config-validation"],"backgroundTag":"empty-required-field","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}