{"record":{"id":"c665d11095eac5ab","repo":"siyuan-note/siyuan","slug":"empty-result","errorCode":null,"errorMessage":"empty result","messagePattern":"empty result","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/mcp/tools/block.go","lineNumber":430,"sourceCode":"\t}\n\treturn CallToolResult{Content: []ContentItem{{Type: \"text\", Text: \"block deleted: \" + id}}}, nil\n}\n\nfunc getBlockData(args map[string]any) (data, dataType string) {\n\tdata, _ = args[\"data\"].(string)\n\tdataType, _ = args[\"dataType\"].(string)\n\tif dataType == \"\" {\n\t\tdataType = \"markdown\"\n\t}\n\treturn\n}\n\nfunc markdownToBlockDOM(md string) (string, error) {\n\tluteEngine := util.NewLute()\n\tluteEngine.SetHTMLTag2TextMark(true)\n\tresult, _ := luteEngine.Md2BlockDOMTree(md, true)\n\tif result == \"\" {\n\t\treturn \"\", fmt.Errorf(\"empty result\")\n\t}\n\treturn result, nil\n}\n\nfunc blockMove(args map[string]any) (CallToolResult, error) {\n\tid, _ := args[\"id\"].(string)\n\tif id == \"\" {\n\t\treturn CallToolResult{Content: []ContentItem{{Type: \"text\", Text: \"id is required\"}}, IsError: true}, nil\n\t}\n\tparentID, _ := args[\"parentID\"].(string)\n\tif parentID == \"\" {\n\t\treturn CallToolResult{Content: []ContentItem{{Type: \"text\", Text: \"parentID is required\"}}, IsError: true}, nil\n\t}\n\tpreviousID, _ := args[\"previousID\"].(string)\n\tboxID, release, scopeErr := beginBlockToolScope(args, true, id, parentID, previousID)\n\tif scopeErr != nil {\n\t\treturn blockToolError(scopeErr.Error())\n\t}","sourceCodeStart":412,"sourceCodeEnd":448,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/mcp/tools/block.go#L412-L448","documentation":"markdownToBlockDOM (kernel/mcp/tools/block.go) converts markdown via Lute's Md2BlockDOMTree; if Lute yields an empty string the tool cannot build a block and returns this error. It guards block insert/append/prepend and HTML-asset iframe creation from writing empty content.","triggerScenarios":"Passing an empty markdown string, or content that Lute reduces to nothing (e.g. only whitespace/HTML tags with SetHTMLTag2TextMark yielding no DOM) to blockInsert, blockAppend, blockPrepend, htmlAssetIFrameBlockDOM or dailynoteAppend.","commonSituations":"An MCP client sends an empty or whitespace-only content field; a script pipes an empty template file; HTML-only content stripped to nothing by the tag-to-textmark conversion.","solutions":["Provide non-empty markdown content in the tool call","Trim-check content client-side before invoking and skip the call if blank","If content is raw HTML, wrap it in a fenced HTML or kramdown block so Lute produces a DOM","Check the returned error path: if Lute silently fails, log the input markdown to find the culprit"],"exampleFix":"// before\nawait call(\"insert_block\", {content: \"\"})\n// after\nif (!md.trim()) throw new Error(\"refusing to insert empty markdown\")\nawait call(\"insert_block\", {content: md})","handlingStrategy":"validation","validationCode":"if (typeof md !== \"string\" || !md.trim()) throw new Error(\"markdown content required\");","typeGuard":"const isNonEmptyString = (s) => typeof s === \"string\" && s.trim().length > 0;","tryCatchPattern":"try { await call(\"block_insert\", {content: md}) } catch (e) { if (String(e).includes(\"empty result\")) { console.error(\"markdown produced no DOM:\", md); } }","preventionTips":["Never pass empty or whitespace-only markdown","Wrap raw HTML in fenced blocks","Log the markdown input when this error occurs"],"tags":["mcp","markdown","lute","empty-input"],"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"}