siyuan-note/siyuan · error

empty result

Error message

empty result

What it means

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.

Solutions

  1. Provide non-empty markdown content in the tool call
  2. Trim-check content client-side before invoking and skip the call if blank
  3. If content is raw HTML, wrap it in a fenced HTML or kramdown block so Lute produces a DOM
  4. Check the returned error path: if Lute silently fails, log the input markdown to find the culprit

Example fix

// before
await call("insert_block", {content: ""})
// after
if (!md.trim()) throw new Error("refusing to insert empty markdown")
await call("insert_block", {content: md})
Defensive patterns

Strategy: validation

Validate before calling

if (typeof md !== "string" || !md.trim()) throw new Error("markdown content required");

Type guard

const isNonEmptyString = (s) => typeof s === "string" && s.trim().length > 0;

Try / catch

try { await call("block_insert", {content: md}) } catch (e) { if (String(e).includes("empty result")) { console.error("markdown produced no DOM:", md); } }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/c665d11095eac5ab. Report an issue: GitHub.

Appendix: source

Thrown at kernel/mcp/tools/block.go:430

	}
	return CallToolResult{Content: []ContentItem{{Type: "text", Text: "block deleted: " + id}}}, nil
}

func getBlockData(args map[string]any) (data, dataType string) {
	data, _ = args["data"].(string)
	dataType, _ = args["dataType"].(string)
	if dataType == "" {
		dataType = "markdown"
	}
	return
}

func markdownToBlockDOM(md string) (string, error) {
	luteEngine := util.NewLute()
	luteEngine.SetHTMLTag2TextMark(true)
	result, _ := luteEngine.Md2BlockDOMTree(md, true)
	if result == "" {
		return "", fmt.Errorf("empty result")
	}
	return result, nil
}

func blockMove(args map[string]any) (CallToolResult, error) {
	id, _ := args["id"].(string)
	if id == "" {
		return CallToolResult{Content: []ContentItem{{Type: "text", Text: "id is required"}}, IsError: true}, nil
	}
	parentID, _ := args["parentID"].(string)
	if parentID == "" {
		return CallToolResult{Content: []ContentItem{{Type: "text", Text: "parentID is required"}}, IsError: true}, nil
	}
	previousID, _ := args["previousID"].(string)
	boxID, release, scopeErr := beginBlockToolScope(args, true, id, parentID, previousID)
	if scopeErr != nil {
		return blockToolError(scopeErr.Error())
	}

View on GitHub (pinned to 9f775e8a12)